Python学习笔记(9),Python面向对象高级特性2 -- 定制类和多重继承

Phylicia ·
更新时间:2024-09-20
· 888 次阅读

多重继承

Python与Java不同,Python可以多重继承,在设计类时,可以考虑MixIn设计,一个类继承多个类,使其具有多个功能。

定制类

介绍了以下几种类的方法:
__str__(), __iter__(), __next__(), __getitem__(), __getattr__(), __call__()

__str__类似java中的toString()方法,print一个类调用的就是类的__str__方法。

class Student: def __init__(self, name): self.name = name def __str__(self): return 'Student object (name: %s)' % self.name print(Student('Michael')) Student object (name: Michael)

直接显示变量调用的不是__str__(),而是__repr__()__str__返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,用于调式服务。通常两个方法代码一样

class Student: def __init__(self, name): self.name = name def __str__(self): return 'Student object (name: %s)' % self.name __repr__ = __str__

__iter__类似于Java中的Iterator。一个类想被用于for循环,就必须实现一个__iter__()方法。该方法返回一个迭代对象,循环不断调用__next__方法获取下一个值,直到遇到StopIteration错误时退出循环。

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 > 20: raise StopIteration() return self.a for n in Fib(): print(n) 1 1 2 3 5 8 13

__getitem__可以使该类像list一样取第几个元素

def getitem(self, n): # 定义getitem函数,也可以在函数定义内定义 a, b = 1, 1 for x in range(n): a, b = b, a+b return n Fib.__getitem__ = getitem # 添加__getitem__ 方法。 f = Fib() print(f[0]) print(f[5]) 0 5

如果想要实现切片的方法,需要对__getitem__()方法进一步改进
类似,如果把对象看成是dict__getitem__()的参数是一个可以作为keyobject,例如str。与之对应有__setitem__()__delitem()__方法。
这些方法可以让自己定义的类和Python自带的list, tuple, dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

__getattr__()方法,动态返回一个属性

class Student2: def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr == 'score': return 99

当调用的属性不存在时,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样就有机会返回score的值

s = Student2() print(s.name) print(s.score) print(s.zipcode) Michael 99 None

__getattr__也可以返回函数。
只有在没有找到属性的情况下,才会调用__getattr__,如果是已有属性,不会在__getattr__中查找。
没有定义的调用s.zipcode会返回None,因为__getattr__默认返回就是None
为了避免这种情况,让类只响应几个特定属性,需要抛出AttributeError错误。

class Student3: def __getattr__(self, attr): if attr == 'age': return lambda: 25 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr) s3 = Student3() s3.abc --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) in 6 7 s3 = Student3() ----> 8 s3.abc in __getattr__(self, attr) 3 if attr == 'age': 4 return lambda: 25 ----> 5 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr) 6 7 s3 = Student3() AttributeError: 'Student' object has no attribute 'abc'

把一个类的所有方法和属性动态处理了。可针对完全动态地情况作调用。

例如,利用完全动态地__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__ chain = Chain() # create a new instance print(chain.status) # this will call __getattr__('/status) print(chain.user) # this will call __getattr__('/user') print(chain.status.user.timeline) /status /user /status/user/timeline

解析
__getattr__('/status') will call Chain('/status), now, _path = '/status'
then __getattr__('user') will call Chain('/status/user'), now, _path = '/status/user'
continue this chain, _path variable can be dynamically setted.

Chain().user.fan.documents.markdown_file # since __repr__ = __str__, we can directly print it without print() method. /user/fan/documents/markdown_file

__call__可以直接对实例进行调用

class Student4: def __init__(self, name): self.name = name def __call__(self): print('My name is %s' % self.name) s4 = Student4('Fan') s4() My name is Fan

__call__还可以定义参数,对实例进行直接调用就和对一个函数进行调用一样,所以我们完全可以把对象看成函数,把函数看成是对象,因为两者根本就没啥区别。

如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数是Callable的,它可以被调用,同样我们上面定义的带有__call__()的类实例也是Callable的。

callable(max) # True, 函数是callable的 callable(Student4('Fan')) # True Student4的实例是callable的 callable('str') # False, 一个string不能被调用,不是callable False 焦下鹿 原创文章 48获赞 0访问量 1269 关注 私信 展开阅读全文
作者:焦下鹿



python面向对象 python学习 继承 对象 多重继承 Python

需要 登录 后方可回复, 如果你还没有账号请 注册新账号