总目录:https://www.jianshu.com/p/e406a9bc93a9
Python - 子目录:https://www.jianshu.com/p/50b432cb9460
面向对象还有很多高级特性,允许我们写出非常强大的功能。
我们会讨论多重继承、定制类、元类等概念。
__slots__
因为业务需求,我们会给一个类绑定很多变量或者方法,但是如果我们想要限制实例的属性呢?这就需要我们的 __slots__ .
举一个例子
class Student(object):
__slots__ = ('name','age') # 定义允许绑定的属性名称
之后我们给他绑定属性
s = Student()
s.name ='张三'
s.age =20
s.score =90
AttributeError: 'Student' object has no attribute 'score'
由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
同样有一点要注意的
class GraduateStudent(Student):
pass
g = GraduateStudent()
g.score =9999
__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。
除非子类也定义__slots__,这样子类也会继承父类的__slots__。
@property
__slots__用来限制类的属性,那么@property是用来显示类的属性的值的。
在上面我们给score赋了一个9999的值,这显然不符合实际,我们首先可以用getXX和setXX的方法来拒绝直接将变量公布。
class Student(object):
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value <0 or value >100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.set_score(60)
print(s.get_score())
60
s.set_score(9999)
...
ValueError: score must between 0 ~ 100!
这样可以避免变量的公布,但是违背了Python的一个原则:优雅,所以Python有一个@property的内置装饰器 。
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value <0 or value >100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.set_score(60)
print(s.get_score())
60
s.set_score(9999)
...
ValueError: score must between 0 ~ 100!
@property将getter方法(这里指第一个score)变成了属性,同时生成了另一个装饰器@score.setter,这个装饰器负责把一个setter方法(这里指第二个score)变成属性赋值。
这里还有一个@property的特性,如果只有@property没有@XXX.setter,那么这个属性就变成了只读属性。
多重继承
我们在2.4.1Python-类基础中说过继承,我们在扩展一下这个问题。
我们将动物增加到四个
Dog - 狗;
Bat - 蝙蝠;
Parrot - 鹦鹉;
Ostrich - 鸵鸟。
同时增加哺乳动物和鸟类的类。
class Animal(object):
pass
# 大类:
class Mammal(Animal):
pass
class Bird(Animal):
pass
# 各种动物:
class Dog(Mammal):
pass
class Bat(Mammal):
pass
class Parrot(Bird):
pass
class Ostrich(Bird):
pass
之后我们增加运动类
#动作
class Runnable(object):
def run(self):
print('Running...')
class Flyable(object):
def fly(self):
print('Flying...')
这样我们只需要让动物类多继承一个动作类即可。
class Animal(object):
pass
# 大类:
class Mammal(Animal):
pass
class Bird(Animal):
pass
#动作
class Runnable(object):
def run(self):
print('Running...')
class Flyable(object):
def fly(self):
print('Flying...')
# 各种动物:
class Dog(Mammal,Runnable):
pass
class Bat(Mammal,Flyable):
pass
class Parrot(Bird,Flyable):
pass
class Ostrich(Bird,Runnable):
pass
在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。
参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017501628721248
定制类(魔法函数)
我们之前讲过,__init__,__slots__这样class自带的函数,其实class内部还自带很多这样的方法:
__str__
让类和对象一样能够进行友好的字符串打印。
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return "Student's name %s" %self.name
print(Student('Michael'))
Student's name Michael
这样就能简单的打印出来好看的字符串了。
__iter__
返回一个新的迭代器对象
class Fib(object):
def __init__(self):
self.a, self.b =0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b =self.b, self.a +self.b# 计算下一个值
if self.a >100:# 退出循环的条件
raise StopIteration()
return self.a# 返回下一个值
for fin Fib():
print(f)
1123581321345589
__getitem__
让类和序列一样能够进行切片操作
class Fib(object):
def __getitem__(self, n):
a, b =1, 1
for xin range(n):
a, b = b, a + b
return a
print(Fib()[5])
8
__getitem__也生成了一个类似__iter__的迭代器对象,但是可以通过切片操作获取对应的元素。
__getattr__
让类和对象一样进行异常处理
class Student(object):
def __init__(self):
self.name ='Michael'
def __getattr__(self, attr):
if attr=='score':
return '没有score属性'
s=Student()
print(s.score)
没有score属性
调用score属性,但是类中没有这个属性,如果不定义__getattr__,那么就会报错。
__call__
让实例可以直接调用
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' %self.name)
s=Student('张三')
s()
My name is 张三.
使用枚举类
如果我们需要大量常量,更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能。
Enum()
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, memberin Month.__members__.items():
print(name, '=>', member, ',', member.value)
Enum()可以帮助我们完成枚举类型的构造,除此之外,还有枚举类:
@unique
from enumimport Enum, unique
@unique
class Weekday(Enum):
Sun =0 # Sun的value被设定为0
Mon =1
Tue =2
Wed =3
Thu =4
Fri =5
Sat =6
day1 = Weekday.Mon
print(day1)
print(Weekday(2))
print(Weekday.Wed)
print(Weekday['Thu'])
Weekday.Mon
Weekday.Tue
Weekday.Wed
Weekday.Thu
print(Weekday.Tue.value)
2
print(day1 == Weekday.Mon)
print(day1 == Weekday(1))
True
True
这是几种获取元素的方法。
参考:https://docs.python.org/3/reference/datamodel.html#special-method-names