类的高级特性
访问控制
-
变量名类似__xxx__ 的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__ 、__score__ 这样的变量名。 -
以一个下划线开头的实例变量名,比如_name ,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。 -
如果以__ 开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name 是因为Python解释器对外把__name 变量改成了_Student__name ,所以,仍然可以通过_Student__name 来访问__name 变量,但不建议这样做。
继承和多态
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
def run_twice(animal):
animal.run()
animal.run()
新增一个Animal 的子类,不必对run_twice() 做任何修改,只要确保run() 方法编写正确,不用管原来的代码是如何调用的,原因就在于多态。甚至不必须传入Animal 类型。我们只需要保证传入的对象有一个run() 方法就可以了。
对扩展开放:允许新增Animal 子类;
对修改封闭:不需要修改依赖Animal 类型的run_twice() 等函数。
获取对象信息
>>> type(123)
<class 'int'>
>>> isinstance(123, int)
True
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
>>> hasattr(obj, 'y')
False
>>> setattr(obj, 'y', 19)
>>> getattr(obj, 'y')
19
实例属性和类属性
实例属性属于各个实例所有,互不干扰,通过self或实例变量访问。
类属性属于类所有,所有实例共享一个属性,通过类名访问。
不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。
class Student(object):
count = 0
def __init__(self, name):
self.name = name
Student.count+=1
高级特性
__slots__
定义一个特殊的__slots__ 变量,来限制该class实例能添加的属性,只有放入__slots__ 的属性可以被动态绑定。
class Student(object):
__slots__ = ('name', 'age')
>>> s.age = 25
>>> s.score = 99
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
__slots__ 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:除非在子类中也定义__slots__ ,这样,子类实例允许定义的属性就是自身的__slots__ 加上父类的__slots__ 。
@property
@property 装饰器负责把一个方法变成属性调用
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2021 - self._birth
>>> s=Student()
>>> s.birth=2001
>>> s.age
20
上面的birth 是可读写属性,而age 就是一个只读属性
多重继承
通过多重继承,一个子类可以同时获得多个父类的所有功能。
class Dog(Mammal, Runnable):
pass
MixIn 的目的就是给一个类增加多个功能。在设计类的时候,优先考虑通过多重继承来组合多个MixIn 的功能,而不是设计多层次的复杂的继承关系。
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
枚举类
from enum import Enum,unique
@unique
class Gender(Enum):
Male = 0
Female = 1
类的常见特殊方法
用print打印一个实例时,调用__str__() ,返回一个字符串
>>> print(Student('A'))
Student object (name: A)
__repr__() ,直接显示变量调用,为调试服务
>>> s
Student object (name: A)
-
__iter__() 方法,该方法返回一个迭代对象,例如在for循环开始时调用 -
__getitem__(self,n) 按照下标取出元素 -
__getattr__() 方法,动态返回一个属性。当调用不存在的属性时调用该方法。 class Chain(object):
def __init__(self, path=''):
self._path = path
def __getattr__(self, path):
return Chain('%s/%s' % (self._path, path))
def __str__(self):
return self._path
__repr__ = __str__
>>> Chain().status.user.timeline.list
'/status/user/timeline/list'
-
__call__() 直接对实例进行调用
>>> s()
My name is Michael.
class Chain(object):
def __init__(self, path=''):
self.__path = path
def __getattr__(self, path):
return Chain('%s/%s' % (self.__path, path))
def __call__(self, path):
return Chain('%s/%s' % (self.__path, path))
def __str__(self):
return self.__path
__repr__ = __str__
>>> Chain().users('michael').repos
/users/michael/repos
-
instance.method() 调用实例方法 -
callable() 判断对象是否是可调用对象
对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
|