面向对象
Python中对象的概念很广泛,Python中的一切内容都可以称为对象,除了数字、字符串、列表、元组、字典、集合、range对象、zip对象等等,函数也是对象,类也是对象。
在Python中,可以使用内置方法isinstance()来测试一个对象是否为某个类的实例。
>>> isinstance(car, Car)
True
>>> isinstance(car, str)
False
在Python中比较特殊的是,可以动态地为类和对象增加成员,这一点是和很多面向对象程序设计语言不同的,也是Python动态类型特点的一种重要体现。
私有成员
Python并没有对私有成员提供严格的访问保护机制。
在定义类的成员时,如果成员名以两个下划线“__”开头则表示是私有成员。在内部,python使用一种name mangling技术,将__membername替换成_classname__membername,所以你在外部使用原来的私有成员的名字时,会提示找不到。
私有成员在类的外部不能直接访问,Python的私有成员并不是真正意义上的私有,需要通过调用对象的公有成员方法来访问,也可以通过Python支持的特殊方式来访问。
在Python中,以下划线开头的变量名和方法名有特殊的含义,尤其是在类的定义中。用下划线作为变量名和方法名前缀和后缀来表示类的特殊成员:
_xxx:受保护成员,不能用'from module import *'导入;
__xxx__:系统定义的特殊成员;
__xxx:私有成员,只有类对象自己能访问,子类对象不能直接访问到这个成员,但在对象外部可以通过“对象名._类名__xxx”这样的特殊方式来访问。
方法
在类中定义的方法可以粗略分为四大类:公有方法、私有方法、静态方法和类方法。
公有方法、私有方法都属于对象,私有方法的名字以两个下划线“__”开始,每个对象都有自己的公有方法和私有方法,在这两类方法中可以访问属于类和对象的成员;
公有方法通过对象名直接调用,私有方法不能通过对象名直接调用,只能在属于对象的方法中通过self调用或在外部通过Python支持的特殊方式来调用。
如果通过类名来调用属于对象的公有方法,需要显式为该方法的self参数传递一个对象名,用来明确指定访问哪个对象的数据成员。
静态方法和类方法都可以通过类名和对象名调用,但不能直接访问属于对象的成员,只能访问属于类的成员。
静态方法可以没有参数。
一般将cls作为类方法的第一个参数名称,但也可以使用其他的名字作为参数,并且在调用类方法时不需要为该参数传递值。
class Root: __total = 0 # 私有成员变量 def __init__(self, v): # 构造方法 self.__value = v Root.__total += 1 # 类中成员属性属于类,需要用 类名. 来调用 def show(self): # 普通实例方法 print('self.__value:', self.__value) print('Root.__total:', Root.__total) @classmethod # 装饰器,声明类方法 def classShowTotal(cls): # 类方法 print(cls.__total) @staticmethod # 装饰器,声明静态方法 def staticShowTotal(): # 静态方法 print(Root.__total) r = Root(3) r.classShowTotal() # 通过对象来调用类方法 r.staticShowTotal() # 通过对象来调用静态方法 r.show() rr = Root(5) Root.classShowTotal() # 通过类名调用类方法 Root.staticShowTotal() # 通过类名调用静态方法 rr.show() ''' 1 1 self.__value: 3 Root.__total: 1 2 2 self.__value: 5 Root.__total: 2 '''
@property把方法编程可调用的属性
class Student(object): '''测试property装饰器''' @property def birth(self): # 此为getter方法 return self._birth @birth.setter def birth(self, value): # 此为setter方法 self._birth = value @birth.deleter def birth(self): print('调用birth的deleter方法') del self._birth @property def age(self): # 没有为age设置setter方法,因此age是一个只读属性 return 2018 - self._birth s = Student() s.birth = 1997 # 加上@property装饰器后,birth和age都变成属性了,age没有定义setter方法,因此为只读属性 print(s.birth) # 1997 print(s.age) # 21 del s.birth # 调用birth的deleter方法
python类的特殊方法
方法 |
功能说明 |
__new__() |
类的静态方法,用于确定是否要创建对象 |
__init__() |
构造方法,创建对象时自动调用 |
__del__() |
析构方法,释放对象时自动调用 |
__add__() |
+ |
__sub__() |
- |
__mul__() |
* |
__truediv__() |
/ |
__floordiv__() |
// |
__mod__() |
% |
__pow__() |
** |
__eq__()、 __ne__()、__lt__()、 __le__()、__gt__()、 __ge__() |
==、 !=、<、 <=、>、 >= |
__lshift__()、__rshift__() |
<<、>> |
__and__()、__or__()、__invert__()、__xor__() |
&、|、~、^ |
__iadd__()、__isub__() |
+=、-=,很多其他运算符也有与之对应的复合赋值运算符 |
__pos__() |
一元运算符+,正号 |
__neg__() |
一元运算符-,负号 |
__contains__ () |
与成员测试运算符in对应 |
__radd__()、__rsub__ |
反射加法、反射减法,一般与普通加法和减法具有相同的功能,但操作数的位置或顺序相反,很多其他运算符也有与之对应的反射运算符 |
__abs__() |
与内置函数abs()对应 |
__bool__() |
与内置函数bool()对应,要求该方法必须返回True或False |
__bytes__() |
与内置函数bytes()对应 |
__complex__() |
与内置函数complex()对应,要求该方法必须返回复数 |
__dir__() |
与内置函数dir()对应 |
__divmod__() |
与内置函数divmod()对应 |
__float__() |
与内置函数float()对应,要求该该方法必须返回实数 |
__hash__() |
与内置函数hash()对应 |
__int__() |
与内置函数int()对应,要求该方法必须返回整数 |
__len__() |
与内置函数len()对应 |
__next__() |
与内置函数next()对应 |
__reduce__() |
提供对reduce()函数的支持 |
__reversed__() |
与内置函数reversed()对应 |
__round__() |
对内置函数round()对应 |
__str__() |
与内置函数str()对应,要求该方法必须返回str类型的数据 |
__repr__() |
打印、转换,要求该方法必须返回str类型的数据 |
__getitem__() |
按照索引获取值 |
__setitem__() |
按照索引赋值 |
__delattr__() |
删除对象的指定属性 |
__getattr__() |
获取对象指定属性的值,对应成员访问运算符“.” |
__getattribute__() |
获取对象指定属性的值,如果同时定义了该方法与__getattr__(),那么__getattr__()将不会被调用,除非在__getattribute__()中显式调用__getattr__()或者抛出AttributeError异常 |
__setattr__() |
设置对象指定属性的值 |
__base__ |
该类的基类 |
__class__ |
返回对象所属的类 |
__dict__ |
对象所包含的属性与值的字典 |
__subclasses__() |
返回该类的所有子类 |
__call__() |
包含该特殊方法的类的实例可以像函数一样调用 |
__get__() |
定义了这三个特殊方法中任何一个的类称作描述符(descriptor),描述符对象一般作为其他类的属性来使用,这三个方法分别在获取属性、修改属性值或删除属性时被调用 |
__set__() |
|
__delete__() |
继承
继承是为代码复用和设计复用而设计的,是面向对象程序设计的重要特性之一。设计一个新类时,如果可以继承一个已有的设计良好的类然后进行二次开发,无疑会大幅度减少开发工作量。
在继承关系中,已有的、设计好的类称为父类或基类,新设计的类称为子类或派生类。派生类可以继承父类的公有成员,但是不能继承其私有成员。如果需要在派生类中调用基类的方法,可以使用内置函数super()或者通过“基类名.方法名()”的方式来实现这一目的。
Python支持多继承,如果父类中有相同的方法名,而在子类中使用时没有指定父类名,则Python解释器将从左向右按顺序进行搜索。
class A(object): def __init__(self): self.__private() self.public() def __private(self): print('__private() method in A') def public(self): print('public() method in A') class B(A): ''' 注意,类B没有定义构造函数,会调用父类A的构造函数,私有方法使用父类A的,普通公开方法使用的是自己的 ''' def __private(self): print('__private() method in B') def public(self): print('public() method in B') class C(A): ''' 显式定义了构造函数,直接调用自己定义的构造函数,而不是调用父类的构造函数。 私有方法和普通公开方法都是使用自己的。 ''' def __init__(self): # 显式定义构造函数 self.__private() self.public() def __private(self): print('__private() method in C') def public(self): print('public() method in C') b = B() c = C() ''' __private() method in A public() method in B __private() method in C public() method in C '''
多态
所谓多态(polymorphism),是指基类的同一个方法在不同派生类对象中具有不同的表现和行为。派生类继承了基类行为和属性之后,还会增加某些特定的行为和属性,同时还可能会对继承来的某些行为进行一定的改变,这都是多态的表现形式。
Python大多数运算符可以作用于多种不同类型的操作数,并且对于不同类型的操作数往往有不同的表现,这本身就是多态,是通过特殊方法与运算符重载实现的。
class Animal(object): # 定义基类 def show(self): print('I am an animal.') class Cat(Animal): # 派生类,覆盖了基类的show()方法 def show(self): print('I am a cat.') class Dog(Animal): # 派生类 def show(self): print('I am a dog.') class Tiger(Animal): # 派生类 def show(self): print('I am a tiger.') class Test(Animal): # 派生类,没有覆盖基类的show()方法 pass x = [item() for item in (Animal, Cat, Dog, Tiger, Test)] for item in x: # 遍历基类和派生类对象并调用show()方法 item.show() ''' I am an animal. I am a cat. I am a dog. I am a tiger. I am an animal. '''