
Python类与面向对象:实战中的核心知识
Python类与面向对象:实战中的核心知识
在我五年的测试开发工作中,面向对象编程一直是构建复杂系统的重要基石。Python的类机制既简洁又强大,让我们能够以更加自然的方式组织和管理代码。
这篇文章不会面面俱到地介绍每个细节,而是聚焦于我在实际工作中最常用、最实用的知识点。通过这些内容,你可以快速掌握Python类的核心概念,并在实际项目中灵活运用。
类的定义
class ClassName:
"""docstring for ClassName"""
def __init__(self, arg):
super(ClassName, self).__init__()
self.arg = arg控制对象的生成过程 new
new 是用来控制对象的生成过程的,在对象生成之间调用,如果__new__ 方法不返回 对象,则不会调用__init__
class Singleton(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls, *args, **kw)
return cls._instance
class MyClass(Singleton):
a = 1new 的使用场景:
- 单例模式:确保每次只创建一个实例。
- 动态返回不同类:根据条件返回不同类型的对象。
- 不可变类型:在创建不可变对象时使用。
- 资源管理:控制资源的分配和管理。 在我的工作过程中,也就使用过单例模式,下面是一个单例模式的例子:
class SingleClass(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
# cls._instance = object.__new__(cls,*args,**kwargs)
cls._instance = super().__new__(cls,*args,**kwargs)
return cls._instance
else:
return cls._instance
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
pass验证单例模式:
my_class = SingleClass()
my_class2 = SingleClass()
print(my_class is my_class2)结果:
True单例模式的应用场景:
单例模式应用的场景一般发现在以下条件下:
资源共享的情况下,避免由于资源操作时导致的性能或损耗等,如日志文件,应用配置。
控制资源的情况下,方便资源之间的互相通信。如线程池等,
- 网站的计数器
- 应用配置
- 多线程池
- 数据库配置 数据库连接池
- 应用程序的日志应用...
初始化实例属性 init
init 是在对象创建之后调用,可以用来初始化对象的属性
class MyClass(object):
def __init__(self, a):
self.a = a__new__和__init__的区别
在我的工作过程中真的很少使用new,也就单例使用过 __new__是一个静态方法,而__init__是一个实例方法.
__new__方法会返回一个创建的实例,而__init__什么都不返回.
只有在__new__返回一个cls的实例时后面的__init__才能被调用.
当创建一个新实例时调用__new__,初始化一个实例时用__init__.
实例化对象
my_class = MyClass(1)实例属性和类属性
class MyClass(object):
a = 1
def __init__(self, a):
self.a = a注意:
- 实例属性的优先级高于类属性
- 类没有实例属性时会调用类属性
面向对象中怎么实现只读属性?
将对象私有化,通过共有方法提供一个读取数据的接口
class Person:
def __init__(self, name, age): # 初始化方法
self.__name = name
self.age = age
def get_name(self):
return self.__name
man = Person('ma', 18)
print(man.get_name())结果:
ma最好的方法
class Person:
def __init__(self, name, age): # 初始化方法
self.__name = name
self.age = age
@property
def name(self):
return self.__name
man = Person('ma', 18)
print(man.name)结果:
ma静态方法和类方法
class Person:
sex = 'male'
def __init__(self, name, age): # 初始化方法
self.name = name
self.age = age
@staticmethod
def eat(thing):
print(f'eat {thing}')
print(Person.sex)
@classmethod
def drink(cls, thing):
print(f'drink {thing}')
print(cls.sex)
def run(self):
print('run')
man = Person('ma', 18)
print(man.name)
print(man.age)
print(man.sex)
man.eat('egg')
Person.eat('egg')
Person.drink('water')结果:
ma
18
male
eat egg
male
eat egg
male
drink water
male静态方法和类方法的区别在于:
- 静态方法没有 self 参数,类方法有 cls 参数
- 静态方法可以通过类名调用,类方法可以通过类名调用
- 静态方法可以通过实例调用,类方法可以通过实例调用 什么时候使用静态方法和类方法?
- 静态方法:当方法中不需要使用实例属性时,可以使用静态方法
- 类方法:当方法中需要使用类属性时,可以使用类方法
访问限定
- 公有属性:可以直接访问
- 私有属性 __attrname :一般不可访问,强行访问的话,可以通过 实例._类名 __attrname 来访问,可以通过 getter 和 setter 方法访问
- 伪私有属性_attrname :可以访问,但不要随意访问
class Person:
sex = 'male'
def __init__(self, name, age,student): # 初始化方法
self.__name = name
self.age = age
self._student = student
def get_fake_student(self):
return self._student
def get_private(self):
return self.__name
man = Person('ma', 18,True)
print(man.get_private())
print(man.get_fake_student())
print(man.age)
print(man._student)
print(man.__name) # 报错
print(man._Person__name) # 可以访问结果:
ma
True
18
True
Traceback (most recent call last):
File "D:\code\python\test.py", line 19, in <module>
print(man.__name) # 报错
AttributeError: 'Person' object has no attribute '__name'
mapython面向对象的一点理解
面向对象是相对于面相过程而言的,他们是两种编程范式
- 面向过程:把完成某一个需求的 所有步骤 从头到尾 逐步实现
- 面向对象:把完成某一个需求的 所有步骤 拆分成 多个 小步骤,通过调用 不同功能的实现函数,让这些 小步骤 依次执行
面向对象的特点:
- 更符合我们人类的思维习惯
- 让复杂的事情变得简单起来
- 面向对象的程序开发,从宏观上把握、从整体上合理分析,从细节上把握,进行程序的编写。 面向过程举例
def add(x, y):
return x + y
def multiply(x, y):
return x * y
result = add(2, 3)
print(result) # 输出:5
result = multiply(2, 3)
print(result) # 输出:6面对对象
class Calculator:
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
calc = Calculator()
result = calc.add(2, 3)
print(result) # 输出:5
result = calc.multiply(2, 3)
print(result) # 输出:6在面向对象语言中有一个很重 要的东西,叫做类。面向对象有三大特性:封装、继承、多态。 封装:封装是面向对象的核心思想,将对象的属性和行为封装起来,对外只暴露 接口,隐藏内部实现细节。 继承:继承是面向对象的另一个重要特性,通过继承可以实现代码的复用,提高 代码的可维护性。 多态:多态是面向对象的最后一个特性,指的是同一个方法在不同的对象上有不同的行为。
继承
继承的原则:
- 子类可以继承父类的所有属性和方法
- 子类可以重写父类的方法
- 子类可以添加新的属性和方法
class Person:
sex = 'male'
def __init__(self, name, age): # 初始化方法
self.name = name
self.age = age
def eat(self):
print('eat')
def sleep(self):
print('sleep')
class Student(Person):
def __init__(self, name, age, student): # 初始化方法
super().__init__(name, age)
self.student = student
def eat(self):
print('eat')
print(self.student)
def sleep(self):
print('sleep')
print(self.student)
def study(self):
print('study')
man = Student('ma', 18,True)
print(man.name)
print(man.age)
print(man.student)
man.eat()
man.sleep()
man.study()结果:
ma
18
True
eat
True
sleep
True
study多继承
class Person:
sex = 'male'
def __init__(self, name, age): # 初始化方法
self.name = name
self.age = age
def eat(self):
print('eat')
def sleep(self):
print('sleep')
class Student(Person):
def __init__(self, name, age, student): # 初始化方法
super().__init__(name, age)
self.student = student
def eat(self):
print('eat')
print(self.student)
def sleep(self):
print('sleep')
print(self.student)
def study(self):
print('study')
class Teacher(Person):
def __init__(self, name, age, teacher): # 初始化方法
super().__init__(name, age)
self.teacher = teacher
def eat(self):
print('eat')
print(self.teacher)
def sleep(self):
print('sleep')
print(self.teacher)
def teach(self):
print('teach')
class StudentTeacher(Student, Teacher):
def __init__(self, name, age, student, teacher): # 初始化方法
super().__init__(name, age, student)
self.teacher = teacher
def eat(self):
print('eat')
print(self.student)
print(self.teacher)
def sleep(self):
print('sleep')
print(self.student)
print(self.teacher)
def study(self):
print('study')
def teach(self):
print('teach')
man = StudentTeacher('ma', 18,True,True)
print(man.name)
print(man.age)
print(man.student)
print(man.teacher)
man.eat()
man.sleep()
man.study()
man.teach()结果:
ma
18
True
True
eat
True
True
sleep
True
True
study
teach多继承的调用顺序:
- 从左到右,从上到下
- 如果有多个父类有相同的方法,那么会调用第一个父类的方法 我们可以使用 mro 方法来查看调用顺序
print(StudentTeacher.mro())结果:
[<class '__main__.StudentTeacher'>, <class '__main__.Student'>, <class '__main__.Teacher'>, <class '__main__.Person'>, <class 'object'>]多态
什么是多态?
多态是指同一个方法在不同的情况下有不同的实现。
多态的原则:
- 子类可以重写父类的方法
- 子类可以添加新的方法
class Animal:
def run(self):
raise AttributeError('子类必须实现这个方法')
class Dog(Animal):
def run(self):
print('Dog is running')
class Cat(Animal):
def run(self):
print('Cat is running')
dog = Dog()
cat = Cat()
dog.run()
cat.run()结果:
Dog is running
Cat is running注意:
super().init() 不是调用父类的构造函数,而是调用mro下一个类的构造函数
假如父类方法 foo 被子类重写, super().foo() 可以调用父类方法
假如父类方法 foo 没有被子类重写, self.foo() 可以调用父类方法
鸭子类型
偶尔会用到但是用的不多
什么是鸭子类型? 鸭子类型是指如果一个对象看起来像鸭子,那么它就是鸭子。
鸭子类型的原则:
- 如果一个对象看起来像鸭子,那么它就是鸭子。
class Duck:
def quack(self):
print('quack')
def fly(self):
print('fly')
class Person:
def quack(self):
print('person quack')
def fly(self):
print('person not fly')
def quack_and_fly(thing):
thing.quack()
thing.fly()
duck = Duck()
person = Person()
quack_and_fly(duck)
quack_and_fly(person)结果:
quack
fly
person quack
person not fly注意:
- 鸭子类型是一种动态类型,它不需要继承或实现任何接口。
- 鸭子类型是一种代码复用的方式,它可以让不同的对象实现相同的接口。
- 鸭子类型是一种松耦合的方式,它可以让不同的对象实现相同的接口。
再议私有化
- xx: 公有变量
_x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问__xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)__xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:init , __ 不要自己发明这样的名字 xx_:单后置下划线,用于避免与Python关键词的冲突
通过name mangling(名字重整(目的就是以防子类意外重写基类的方法或者属性)如:_Class__object)机制就可以访问private了。
class Person(object):
def __init__(self, name, age, taste):
self.name = name
self._age = age
self.__taste = taste
def showperson(self):
print(self.name)
print(self._age)
print(self.__taste)
def dowork(self):
self._work()
self.__away()
def _work(self):
print('my _work')
def __away(self):
print('my __away')
class Student(Person):
def construction(self, name, age, taste):
self.name = name
self._age = age
self.__taste = taste
def showstudent(self):
print(self.name)
print(self._age)
print(self.__taste)
@staticmethod
def testbug():
_Bug.showbug()
#模块内可以访问,当from cur_module import *时,不导入
class _Bug(object):
@staticmethod
def showbug():
print("showbug")
s1 = Student('jack', 25, 'football')
s1.showperson()
print('*'*20)
#无法访问__taste,导致报错
#s1.showstudent()
s1.construction('rose', 30, 'basketball')
s1.showperson()
print('*'*20)
s1.showstudent()
print('*'*20)
Student.testbug()
总结
- 父类中属性名为
__名字的,子类不继承,子类不能访问 - 如果在子类中向
__名字赋值,那么会在子类中定义的一个与父类相同名字的属性 _名的变量、函数、类在使用from xxx import *时都不会被导入
