今天进入面向对象高级编程!
主要讨论多重继承、定制类、元类等概念。
__slot__限制实例属性
首先来看一下给实例绑定方法和给类绑定方法的不同。
def set_age(self, age): # 定义一个函数作为实例方法,需使用methodtype
self.age = age
from types import MethodType
class Student(object):
pass
s = Student()
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
s.set_age(25) # 调用实例方法
print(s.age) # 测试结果
def set_score(self, score):
self.score = score
Student.set_score = set_score#给类绑定只要一句话
使用__slot__可以限制类下实例的属性的添加!
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student()
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定属性'score'
#报错!无法添加score属性
#但是对于继承的子类来说,这种方法无法限制
class GraduateStudent(Student):
pass
g = GraduateStudent()
g.score = 9999#成功
https://www.liaoxuefeng.com/discuss/969955749132672/1354602591027234
通过MethodType(方法,实例)绑定方法 和 通过函数set_name_(self, name)绑定实例的4种情况对比,写的非常好!可以参考着看看。
@property装饰器
既能检查参数,又可以用类似属性这样简单的方式来访问类的变量
https://www.liaoxuefeng.com/discuss/969955749132672/1353567436800033
如下,两种情况的具体解释!可以参考。
使用property的两种情况:(个人理解是标签作用)
第一种 目的,使函数方法,变成数据属性,方便使用者理解
第二种 目的 为了让隐藏的数据属性按照特定的函数进行调用
练习:请利用@property给一个Screen对象加上width和height属性,以及一个只读属性resolution:
class Screen():#第一种清晰
@property
def width(self):
return self._width
@width.setter
def width(self,width):
self._width=width
@property
def height(self):
return self._height
@height.setter
def height(self,height):
self._height=height
@property
def resolution(self):
return self._width*self._height
#第二种,简单
@property
def resolution(self):
return self.width*self.height
多重继承
过多重继承,一个子类就可以同时获得多个父类的所有功能。
Mixln就是多重继承。只不过Mixln更能直观的了解继承关系。
比如:class Dog(Mammal, Runnable,Flyable):这个多重继承只能看出来继承了三个父类,但是并不知道Dog属于哪个继承链。
class Dog(Mammal, RunnableMixln,FlyableMixln):Mixln设计就能看出来Dog是从属于Mammal继承链,只是增加Runnable和Flyable的功能。
例如:class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
定制类:str,iter,getitem
即一些有特殊含义的类:
比如__str__
规定输出格式
class Student(object):#规定print格式
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
>>> print(Student('Michael'))
Student object (name: Michael)
以及__iter__
使类变成一个迭代器,可以装入for中进行循环
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 > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
#测试
for n in Fib():
... print(n)
1
1
2
3
5
...
46368
还有__getitem__
可以使上文中的Fib按照下标进行读取!
class Fib(object):#为实现切片操作,因此要区分int和slice!
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
以及__getattr__
当调用到不存在的属性的时候,会报错或者返回这个属性中的内容!
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
#测试
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99
在调用API时使用比较方便
重要__call__
call主要的目的是为了让实例化的类也能够被调用!
由于对象和函数的不同,因此如果直接调用实例就会使前面两者界限过于模糊,因此如果类实例加上了call则也可以被调用,不加则有区别!
https://www.liaoxuefeng.com/discuss/969955749132672/1275471364878624
上面的解释,可以看看
实现Chain().users(‘michael’).repos输出/users/michael/repos
枚举类
可以通过num类来实现常量的枚举功能
from enum import Enum, unique
@unique #保证没有重复值
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
元类
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
type就是python的内建元类!类是元类的实例!
而__new__()方法接收到的参数依次是:
当前准备创建的类的对象;
类的名字;
类继承的父类集合;
类的方法集合。
https://www.cnblogs.com/JetpropelledSnake/p/9094103.html#_label2
这一块确实比较难以理解,主要的意思是在一些复杂框架,如ORM中使用会比较方便,意图清晰