(一)使用__slots__
当定义了一个class,创建了一个class的实例后,可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
【正常情况】
from types import MethodType
# 定义class
class Student():
pass
def set_age(self,age):
self.age = age
# 给实例绑定属性
s = Student()
s.name = 'luna'
print(s.name)
# 给实例绑定方法
s.set_age = MethodType(set_age,s)
# 调用实例方法
s.set_age(25)
print(s.age)
# 给class绑定方法
def set_score(self,score):
self.score = score
Student.set_score = set_score # 调用
# Student.set_score=MethodType(set_score,Student) 给类绑定方法
s.set_score(100)
print(s.score)
通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。
【使用__slots__】
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
class Student():
__slots__ = ('name','age') # 用元组定义允许绑定的属性名称
s = Student() # 创建实例
s.name = 'luna' # 绑定属性
s.age = 13
s.score = 90
由于’score’没有被放到__slots__
中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。使用__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
。
(二) 使用@property
Python内置的@property装饰器就是负责把一个方法变成属性调用。
class Student():
@property # 只读属性
def score(self):
return self._score
@score.setter # 可读写属性
def score(self,value):
if not isinstance(value,int):
raise ValueError('必须是整数类型')
if value < 0 or value > 100:
raise ValueError('必须是0-100之间')
self._score = value
s = Student()
s.score = 60
print(s.score)
s.score = 101
print(s.score)
(三)多重继承
继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能。
# 通过多重继承,一个子类就可以同时获得多个父类的所有功能
class Animal():
pass
# 大类
class Mammal(Animal):
pass
class Bird(Animal):
pass
# 功能类
class Runnable():
def run(self):
print('running...')
class Flyable():
def fly(self):
print('flying...')
# 各种动物
class Dog(Mammal,Runnable):
pass
class Parrot(Bird,Flyable):
pass
MixIn
MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系,不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。
(四)定制类
__str__
:返回用户看到的字符串
# __str__
class Student():
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student Object(name:%s)' % self.name
print(Student('luna'))
__iter__
:一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()
方法,返回一个迭代对象,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
# __str__
class Student():
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student Object(name:%s)' % self.name
print(Student('luna'))
# __iter__
class Fib():
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > 1000:
raise StopIteration()
return self.a
def __getitem__(self, n):
if isinstance(n, int): # n是序列
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
for n in Fib():
print(n)
f = Fib()
print(f[3])
# 切片
print(f[0:3])
如果把对象看成dict,__getitem__()
的参数也可能是一个可以作key的object,例如str。与之对应的是__setitem__()
方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()
方法,用于删除某个元素。
__getattr__
:正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错
调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')
来尝试获得属性,这样,我们就有机会返回score的值。
# __getattr__
class Animal():
def __init__(self,name):
self.name = name
def __getattr__(self, item):
if item == 'score':
return '0' # 也可以返回函数
a = Animal('koko')
print(a.name)
print(a.score)
只有在没有找到属性的情况下,才调用__getattr__
,已有的属性,比如name,不会在__getattr__
中查找。
完全动态的__getattr__
,写出一个链式调用:
class Chain():
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__
print(Chain().status.user.timeline.list)
__call__
:任何类,只需要定义一个__call__()
方法,就可以直接对实例进行调用
class Chain():
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__
print(Chain().status.user.timeline.list)
print(Chain().users('miko').repos)
分解 Chain().users('miko').repos
# Chain().users('michael').repos 分解
urls = Chain() # 初始化一个实例
urls = urls.users # 查找实例的一个属性
urls = urls('michael) # 调用一个函数
urls = urls.repos # 还是实例的属性
1、第一步
urls = Chain()
初始化一个实例,此时urls
等于,因为定义了默认值path=''
;
2、第二步4
urls = urls.users
查找urls
的属性users
,没找到定义的属性,那就调用__getattr__
方法,返回了一个函数调用:
def __getattr__(self, users):
return Chain('%s/%s' % (self.__path, users))
这一步调用了Chain()
,而且把要查找的属性users
作为参数传递了进去,也就是Chain(users)
,那么根据Chain()
的逻辑,最后返回的是:/users
,然后跟上一步的结果拼接,最终返回:/users
;
3、第三步
urls = urls('michael')
对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。 原来是为了让实例化对象和函数一样可以被使用。
然后调用urls = urls('michael')
,那么最终返回:/users/michael
4、第四步
urls = u.repos
和第二步没什么区别,所以urls
最为:/users/michael/repos
;
(五)使用枚举类
枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、状态等。
一般就通过字典或类来实现:
使用 Enum
# 使用枚举
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
YELLOW = 3
# 枚举成员有值(默认可重复)、枚举类型不可实例化,不可更改
print(Color.GREEN)
print(repr(Color.GREEN))
print(type(Color.GREEN))
print(isinstance(Color.GREEN, Color))
Color.GREEN
<Color.GREEN: 2>
<enum 'Color'>
True、
定义枚举
1、定义枚举时,成员名不能重复
2、成员值允许相同,第二个成员的名称被视作第一个成员的别名
3、若要不能定义相同的成员值,可以通过 unique 装饰
# 定义枚举
class Code(Enum):
GA = 1
GB = 2
GG = 3
GX = 2 # 成员值允许相同
print(Code.GA.value)
@unique #能定义相同的成员值
class Code2(Enum):
GA = 1
GB = 2
GG = 3
print(Code2.GB.value)
枚举取值
通过成员名来获取成员也可以通过成员值来获取成员:
# 枚举取值
# 获取成员
print(Code2['GA']) # Code2.GA 通过成员名来获取成员
print(Code2(1)) # Code2.GA 通过成员值来获取成员
# 成员属性和值
c = Code2.GA
print(c.name) # GA
print(c.value) # 1
# 支持迭代的方式遍历成员,按定义的顺序,如果有值重复的成员,只获取重复的第一个成员
for cd in Code:
print(cd)
# 特殊属性 __members__ 是一个将名称映射到成员的有序字典,也可以通过它来完成遍历
for code in Code.__members__.items():
print(code)
('GA', <Code.GA: 1>)
('GB', <Code.GB: 2>)
('GG', <Code.GG: 3>)
('GX', <Code.GB: 2>)
枚举比较
枚举的成员可以通过 is 同一性比较或通过 == 等值比较,枚举成员不能进行大小比较:
# 枚举比较
print(Code.GA is Code.GA)
print(Code.GA is not Code.GB)
print(Code.GA ==Code.GA)
print(Code.GA != Code.GB)
扩展枚举 IntEnum
IntEnum 是 Enum 的扩展,不同类型的整数枚举也可以相互比较
from enum import IntEnum
class A(IntEnum):
a = 1
b = 2
class B(IntEnum):
a = 1
b = 1
print(A.a == 1)
print(A.a < 3)
print(A.a == b.A)
print(A.a < B.b)