python第九课 面向对象
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python第九课 面向对象相关的知识,希望对你有一定的参考价值。
上周习题:
1、shape基类,要求所有子类都必须提供面积的计算,子类有三角形,矩形,圆,圆类的数据可序列化
2、用面向对象实现LinkedList链表,单向链表实现append,iternodes
3、双向链表实现append,pop,insert,remove,iternodes
答案:
1、shape类
import math
import json
import msgpack
class Shape:
@property
def area(self):
raise NotImplementedError # 不可实例化的class
class Triangle(Shape):
def __init__(self,a,h):
self.a = a
self.h = h
@property
def area(self):
return int(0.5*self.a*self.h)
class Rectangle(Shape):
def __init__(self,width,height):
self.width = width
self.height = height
@property
def area(self):
return self.width*self.height
class Circle(Shape):
def __init__(self,radius):
self.d = radius ** 2
@property
def area(self):
return ‘{:.3}‘.format(math.pi * self.d)
class SerializableMixin:
def dumps(self,t=‘json‘):
if t == ‘json‘:
return json.dumps(self.__dict__)
elif t == ‘msgpack‘:
return msgpack.packb(self.__dict__)
else:
raise NotImplementedError
class SerializableCircleMixin(SerializableMixin,Circle):
pass
scm = SerializableCircleMixin(4)
print(scm.area)
print(scm.dumps())
# s = scm.dumps(‘msgpack‘)
# print(s)
t = Triangle(3,4)
r = Rectangle(3,4)
c = Circle(3)
print(t.area,r.area,c.area)
2、单链表
class SingleNode:
def __init__(self,item,next=None):
self.item = item # 定义链表单个元素
self.next = next
def __repr__(self):
return repr(self.item) # 返回单个元素
class LinkedList: # 定义链表
def __init__(self):
self.head = None # 定义链表元素开头
self.tail = None # 定义链表元素结尾
def add(self,item): # 实现append
node = SingleNode(item) # 调用singlenode获取一个node
if self.head is None: # 初始head为None
self.head = node # 复制head为node
else:
self.tail.next = node # 后续定义尾部追加next元素
self.tail = node # 更新tail值为node
def iternodes(self): # 迭代
current = self.head # 现在的值为head,第一个
while current:
yield current
current = current.next # 更新current,完成迭代
ll = LinkedList()
ll.add(‘abc‘)
ll.add(1)
ll.add(2)
ll.add(‘def‘)
print(ll.head,ll.tail)
for x in ll.iternodes():
print(x)
3、双向链表;
class SingleNode:
def __init__(self,item,prev=None,next=None):
self.item = item
self.prev = prev
self.next = next
def __repr__(self):
return repr(self.item)
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
def append(self,item):
node = SingleNode(item)
if self.head is None:
self.head = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node
def insert(self,index,item):
if index < 0:
raise ValueError(‘Wrong index {}‘.format(index))
current = None
for i ,node in enumerate(self.iternodes()):
if index == i:
current = node
break
if current is None:
self.append(item)
return
node = SingleNode(item)
prev = current.prev
if prev is None:
self.head = node
else:
prev.next = node
node.prev = prev
node.next = current
current.prev = node
def pop(self):
if self.tail is None:
raise Exception(‘Empty‘)
node = self.tail
item = node.item
prev = node.prev
if prev is None:
self.head = None
self.tail = None
else:
prev.next = None
self.tail = prev
return item
def remove(self,index):
if self.tail is None:
raise Exception(‘Empty‘)
if index < 0:
raise ValueError(‘Wrong index {}‘.format(index))
current = None
for i, node in enumerate(self.iternodes()):
if i == index:
current = node
break
if current is None:
raise ValueError(‘Wrong index {} out of boundary‘.format(index))
prev = current.prev
next = current.next
if prev is None and next is None:
self.head = None
self.tail = None
elif prev is None:
self.head = next
next.prev = None
elif next is None:
self.tail = prev
prev.next = None
else:
prev.next = next
next.prev = prev
del current
def iternodes(self,reverse=False):
current = self.tail if reverse else self.head
while current:
yield current
current = current.prev if reverse else current.next
ll = LinkedList()
ll.append(1)
ll.append(2)
ll.append(3)
ll.append(‘abcd‘)
ll.append(4)
ll.append(5)
ll.append(‘def‘)
ll.append(8)
print(ll.head,ll.tail)
for x in ll.iternodes():
print(x)
print(‘~~~~~~~~~~~~~~~~~~‘)
ll.remove(6)
ll.remove(5)
ll.remove(0)
ll.remove(1)
for x in ll.iternodes():
print(x)
print(‘~~~~~~~~~~~~~~~~~~‘)
ll.insert(3,5)
ll.insert(20,‘def‘)
ll.insert(1,2)
ll.insert(0,‘abc‘)
for x in ll.iternodes():
print(x)
一、魔术方法:
1、特殊属性
属性 | 含义 |
__name__ | 类,函数,方法的名字 |
__moudle__ | 类定义所在的模块名 |
__class__ | 对象或类所属的类 |
__bases__ | 类的基类的元组,顺序为它们在基类列表中表现的顺序 |
__doc__ | 类,函数的文档字符串,如果没有定义则为None |
__mro__ | 类的mro,class.mro( )返回的结果保存在__mro__中 |
__dict__ | 类或实例的属性,可写的字典 |
2、查看属性
__dir__返回类或对象的所有成员名称列表。dir( )函数就是调用__dir__( )。如果提供__dir__( ),则返回属性的列表,否则会尽量在__dict__属性中收集信息
dir()得到的属性多,是个列表
class Person:
pass
p = Person()
print(dir(p))
print(p.__dict__)
import test # test是上面的例子的文档名
from test import Animal
class Cat(Animal):
X = ‘cat‘
y = ‘abcd‘
class Dog(Animal):
def __dir__(self):
return [‘dog‘] # 必须是列表
print(dir())
print(dir(test))
print(sorted(object.__dict__.keys()))
print(dir(Cat))
3、魔术方法:
1、分类:
1、创建与销毁:__init__与__del__
2、hash
3、bool
4、可视化
5、运算符重载
6、容器和大小
7、可调用对象
8、上下文管理
9、反射
10、描述器
11、其他杂项
2、hash
方法:__hash__内建函数hash()调用的返回值,返回一个整数,如果定义这个方法,这个类的实例就可hash
hash取mo法,%x得到的mo,就是hash值,如果x越大,越不容易冲突
hash散列,散开的,很大的一个空间存放,缓存时使用,缓存的目的为了再看,hash算法的时间复杂度时O1,字典和集合的key
class A:
def __init__(self):
self.a = ‘a‘
self.b = ‘b‘
def __hash__(self): hash指向引用,虽然都是1,但内存地址不一样,不是一个
return 1
def __eq__(self, other): eq方法指向内存地址,返回相等,则一模一样
return self.a == other.a
print(hash(A()))
print((A(),A()))
print({A(),A()})
s = {A(),A()}
print(s)
3、eq
方法:__eq__对应==操作符,判断两个对象是否相等,返回bool值
__hash__方法只是返回一个hash值作为set的key,但是去重,还需要判断2个对象是否相等
hash值相等,只是hash冲突,不能说明两个对象是相等的
因此,一般来说,提供__hash__方法是为了作为set或者dict的key的,所以去重要同时进行的话,需要提供eq方法
可hash对象必须提供__hash__方法,没有提供的话,isinstance(p1,collections.hashable)一定为False
去重要提供eq方法
设计二位坐标类Point,比较2个坐标是否相等?
from collections import Hashable
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return 1
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(1,2)
p2 = Point(1,2)
print(p1 is p2) # 内存地址不等 False
print(p1 == p2) # 内容相等 True
print({p1,p2}) 因为eq结果==为True所以去重,因为集合内部实现的是,先看是不是==,是就去重
print(isinstance(p1,Hashable))
4、bool
方法:__bool__内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值
没有定义__bool__( ),就找__len__( )返回长度,非0为真,如果len也没有定义,那么所有实例都返回为真
class A:
pass
print(bool(A()))
class B:
def __bool__(self):
return False
print(bool(B))
print(bool(B()))
class C:
def __len__(self):
return 0
print(bool(C()))
5、可视化
方法 | 意义 |
__repr__ | 内建函数repr( )对一个对象获取字符串表达,如果一个类定义了__repr__但没有定义__str__,那么在请求该类的实例的“非正式”的字符串表示时,也将调用__repr__( ) |
__str__ | str()函数、内建函数format、print()函数调用,需要返回对象的字符串表达 |
__bytes__ | bytes的时候,返回一个对象的bytes表达,即返回bytes对象 |
class A:
def __init__(self):
self.a = ‘a‘
self.b = ‘b‘
def __repr__(self):
return ‘repr:{},{}‘.format(self.a,self.b)
def __str__(self): # 在print,format、str时使用,这三个优先与repr
return ‘str:{},{}‘.format(self.a,self.b)
print(A()) # print函数使用str
print([A()]) # []使用str,但其内部使用repr
print([str(A())]) # []使用str,str也使用str
print(‘str:a,b‘)
s = ‘b‘
print([‘a‘],(s,))
6、运算符重载
去int里找所有的运算符(在pycharm里)
运算符 | 特殊方法 | 含义 |
< <= == > >= != | __lt__ __le__ __eq__ __gt__ __ge__ __ne__ | 比较运算符 |
+ - * / % // ** divmod | __add__ __sub__ __mul__ __truediv__ __mod__ __floordiv__ __pow__ __divmod__ | 算数运算符,移位,位运算也有对应的方法 |
+= -= *= /= %= //= **= | __iadd__ __isub__ __imul__ __itruediv__ __imod__ __ifloordiv __ipow__ |
class A:
def __init__(self,x):
self.x = x
def __sub__(self, other):
return self.x - other.x
def __isub__(self, other):
tmp = self.x - other.x
return A(tmp)
a1 = A(1)
a2 = A(2)
print(a1 - a2)
print(a2.__isub__(a1))
a1 -= a2
print(a1)
练习:完成Point类设计,实现判断点相等的方法,并完成向量的加法
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def add(self, other):
return Point(other.x + self.x, other.y + self.y)
def __add__(self, other):
self.x = self.x + other.x
self.y = self.y + other.y
return Point(self.x, self.y)
def __str__(self):
return ‘Point:{}:{}‘.format(self.x, self.y)
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2)
print(p1 + p2)
print(Point(*(p1, p2)))
print(p1.add(p2))
print(p1)
运算符重载,往往是用面向对象实现的类,需要做大量的运算,而运算符可以实现实例的运算,加方法也可以
7、容器相关方法:
方法 | 意义 |
__len__ | 内建函数len( ),返回对象的长度(>=0的整数),其实即便把对象当作容器 类型看,就如同list或者dict,bool( )函数调用的时候,如果没有__bool__ 方法,则会看__len__方法是否存在,存在则返回非0为真。 |
__iter__ | 迭代容器时调用,返回一个新的迭代器对象,必须时迭代器 |
__contains__ | in成员运算符,没有实现,就调用__iter__方法遍历 |
__getitem__ | 实现self[key]访问。 序列对象,key接受整数为索引,或者切片。对于set和dict,key为hashable。key不存在引发keyerror异常 |
__setitem__ | 和__getitem__的访问类似,是设置值的方法 |
__missing__ | 字典使用__getitem__调用时,key不存在执行该方法 |
练习:将购物车类改造成方便操作的容器类
class Item:
def __init__(self, **kwargs):
self.kwargs = kwargs
def __repr__(self):
return str(sorted(self.kwargs.items()))
class Cart:
def __init__(self):
self.items = []
def additem(self, item):
self.items.append(item)
def __len__(self):
return len(self.items)
def __iter__(self):
return iter(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, key, value):
self.items[key] = value
cart = Cart()
cart.additem(1)
cart.additem(2)
cart.additem(3)
cart.additem(Item(car=‘audi‘, price=270000))
print(len(cart))
for x in cart:
print(x)
print(2 in cart)
cart[2] = ‘a‘
print(cart[2])
8、可调用方法:
方法:__call__ 类的第一个该方法,实例就可以像函数一样调用
可调用对象:定义一个类,并实例化得到其实例,将实例像函数一样调用。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self, *args, **kwargs):
return ‘Point({},{})‘.format(self.x,self.y)
p = Point(4,5)
print(p)
print(p())
class Adder:
def __call__(self, *args, **kwargs):
ret = 0
for x in args:
ret += x
self.ret = ret
return ret
adder = Adder()
print(adder(4, 5))
print(adder.ret)
练习:定义一个斐波那契数列的类,方便调用,计算第n项
class Fib:
def __init__(self):
self.items = [0, 1, 1]
def __call__(self, index):
if index < 0:
return None
if index < len(self.items):
return self.items[index]
for i in range(len(self.items), index+1):
self.items.append(self.items[i-1] + self.items[i-2])
return self.items[index]
def __len__(self):
return len(self.items)
def __iter__(self):
return iter(self.items)
def __str__(self):
return str(self.items)
__repr__ = __str__
fib = Fib()
print(fib(30),len(fib))
for x in fib:
print(x)
增加迭代的方法,返回容器的长度,使用容器减少计算,每次计算后保留容器,下次不用再计算
9、上下文管理:
1、文件IO操作可以对文件对象使用上下文管理,使用with...as语法
2、with open(‘test‘) as f:pass
3、仿照上例,可以写一个类,实现上下文管理
方法 | 意义 |
__enter__ | 进入与此对象相关的上下文,如果存在该方法,with语法会把该 方法的返回值作为绑定到as语句的变量f的值 |
__exit__ | 退出与此对象相关的上下文 |
class Point:
def __init__(self):
print(‘init‘)
def __enter__(self):
print(‘enter‘)
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit‘)
with Point() as f:
print(‘do sth‘)
实例化对象的时候,不会调用enter,进入with语句后,先enter,再with语句块,离开时exit
with可以开启一个上下文运行环境,在执行前做准备工作,执行后做收尾工作
4、上下文管理的安全性
异常对上下文的影响
class Point:
def __init__(self):
print(‘init‘)
def __enter__(self):
print(‘enter‘)
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit‘)
with Point() as f:
raise Exception(‘‘error)
print(‘do sth‘)
enter和exit照样执行,上下文管理很安全
极端例子,import sys后,在with语句中加入sys.exit( )后,依然执行,init,enter,exit函数,哪怕是退出python环境
__enter__方法的return值是with语句的变量,如果不返回就是None,一般返回self
class Point:
def __init__(self):
print(‘init‘)
def __enter__(self):
print(‘enter‘)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(‘exit‘)
p = Point()
with p as f:
print(p == f)
print(‘do sth‘)
5、__enter__和__exit__的参数:
enter没有参数,exit有三个参数,都与异常有关
如果上下文退出时都没有异常,这三个参数都是None,如果有异常,参数意义如下。
如果有异常,参数意义如下:
exc_type,异常类型
exc_value,异常的值
trackback,异常的追踪信息
如果exit方法返回一个等效的True的值,则压制异常,否则,继续抛出异常,默认False
练习:为加法函数计时
1、使用装饰器
2、使用上下文管理
1、装饰器
import datetime
import time
def logger(fn):
def wrapper(*args):
start = datetime.datetime.now()
ret = fn(*args)
delta = datetime.datetime.now()
print(‘{} took {}s‘.format(fn.__name__, (delta-start).total_seconds()))
return ret
return wrapper
@logger
def add(x, y):
time.sleep(2)
return x + y
print(add(1, 2))
2、上下文
import datetime
import time
from functools import wraps
class TimeIt:
"""This is A Class"""
def __init__(self, fn=None):
if fn is not None:
self.fn = fn
wraps(fn)(self)
# self.__doc__ = fn.__doc__
# def __enter__(self):
# self.strat = datetime.datetime.now()
# return self
# 此上下为上下文管理
# def __exit__(self, exc_type, exc_val, exc_tb):
# delta = (datetime.datetime.now() - self.start).total_seconds()
# print(delta)
def __call__(self, *args, **kwargs):
self.start = datetime.datetime.now()
ret = self.fn(*args,**kwargs)
self.delta = (datetime.datetime.now() - self.start).total_seconds()
print(self.delta)
return ret
@TimeIt # add = TimeIt(add)
def add(x, y):
"""This is add function"""
time.sleep(2)
return x + y
print(add(2, 3))
print(add.__doc__)
print(TimeIt().__doc__)
上面代码,既可以用在上下文管理,又可以用作装饰器
上下文管理应用场景:
1、增强功能
在代码执行的前后增强代码,以增强其功能。类似于装饰器的功能
2、资源管理
打开了资源需要关闭,例如文件对象、网络连接、数据库连接等
3、权限验证
在执行代码之前,做权限的验证,在__enter__中处理
二、反射
程序运行时,区别于编译时,指的时程序被加载到内存执行的时候
反射,reflection,指的是运行时,获取类型定义信息。
一个对象,能够在运行时,像照镜子一样,反射出其类型信息
具有反射能力的函数有,type(),isinstance( ),callable(),dir(),getattr()
1、反射相关的函数和方法:
有一个point类,查看它实例的属性,并修改它
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return ‘Point({},{})‘.format(self.x, self.y)
def show(self):
print(self.x, self.y)
p = Point(3, 4)
print(p)
print(p.__dict__)
p.__dict__[‘y‘] = 16
print(p.__dict__)
p.z = 10
print(p.__dict__)
print(dir(p))
print(p.__dir__())
上例通过属性字典__dict__来访问对象的属性,本质也是反射,但是不好看
内建函数 | 意义 |
getattr(object, name[,default]) | 通过name返回object的属性值。当属性不存在,将使用default返回, 如果没有default,则抛出AttrbuteError。name必须是字符串 |
setattr(object, name,value) | object的属性存在,则覆盖,不存在,新增 |
hasattr(object, name) | 判断对象是否有这个名字的属性,name必须是字符串 |
用上面的方法来修改上例的代码
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return ‘Point({}, {})‘.format(self.x, self.y)
def show(self):
print(self)
p1 = Point(1, 2)
p2 = Point(2, 3)
if not hasattr(Point, ‘add‘):
setattr(Point, ‘add‘, lambda self, other: Point(self.x + other.x, self.y + other.y))
print(Point.add)
print(p1.add)
print(p1.add(p2))
这种动态增加属性的方法,和装饰器修饰及Mixin的区别在于运行时动态改变类或者实例,更灵活
练习:命令分发器,通过名称找到对应的函数执行。
class Dispatcher:
def cmd1(self):
print(‘cmd1‘)
def reg(self, cmd, fn): # 注册,实现写入属性
if isinstance(cmd, str):
setattr(self.__class__,cmd,fn)
def run(self): # 运行时,实现获取函数
while True:
cmd = input(‘plz input command:‘)
if cmd.strip() == ‘quit‘:
return
getattr(self, cmd.strip(), self.defaultfn)() # 缺省为函数,需要调用
def defaultfn(self):
# print(‘defalut‘)
print(‘default‘)
dis = Dispatcher()
dis.reg(‘cmd2‘, lambda self: print(1))
dis.reg(‘cmd3‘, lambda self: print(2))
dis.run()
2、反射相关的魔术方法:
__getattr__( ),__steattr__( ),__delattr__( )
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self, x, y):
self.x = x
self.y = y
def show(self):
print(self.x, self.y)
def __getattr__(self, item):
return ‘missing {}‘.format(item)
p1 = Point(4, 5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
一个类的属性会按照继承关系找,如果找不到,就找getattr方法,如果没有这个方法,报attr错误
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self, x, y):
self.x = x
self.y = y
def show(self):
print(self.x, self.y)
def __getattr__(self, item):
return ‘missing {}‘.format(item)
def __setattr__(self, key, value):
print(‘setattr {}={}‘.format(key, value))
self.__dict__[key] = value
p = Point(1, 2)
print(p.x)
p.x = 50
print(p.x)
实例通过. 设置属性,如同self.x = x,就会调用__setattr__,属性要加到dict中,需要自己设置
如果不设置,setattr会拦截对实例属性的增加,修改操作
class Point:
Z = 5
def __init__(self, x, y):
self.x = x
self.y = y
def __delattr__(self, item):
print(‘can not del {}‘.format(item))
p = Point(1, 2)
del p.x
p.z = 15
del p.z
del p.Z
del Point.Z
print(p.Z)
delattr会拦截实例删除属性的操作,但是通过类删除依然可以
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self, x, y):
self.x = x
self.y = y
def __getattr__(self, item):
return ‘missing {}‘.format(item)
def __getattribute__(self, item):
preturn item
p = Point(1, 2)
print(p.__dict__)
print(p.x)
print(p.z)
print(p.m)
print(Point.__dict__)
实例的所有的属性访问,第一个都会调用getattribute方法,它阻止了属性的查找,返回计算后的值,或者抛出一个AttributeError
它的ruturn值将作为属性查找的结果,如果抛出AttributeError,会调用getattr方法,因为表示属性未找到
class Base:
n = 0
class Point(Base):
z = 6
def __init__(self, x, y):
self.x = x
self.y = y
def __getattr__(self, item):
return ‘missing {}‘.format(item)
def __getattribute__(self, item):
return object.__getattribute__(self, item)
p = Point(1, 2)
print(p.__dict__)
print(p.x)
print(p.z)
print(p.m)
print(Point.__dict__)
__getattribute__方法中为了避免该方法中无限的递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如object.getattribute(self,name)
属性查找顺序:实例调用__getattribute__( )-->instance.__dict__-->instance.__class__.__dict__-->继承类的祖先直到object的__dict__-->调用__getattribute__
二、描述器Descriptors
描述器的表现
用到三个魔术方法:__get__( )、__set__( )、__delete__( )
方法如下:
object.__get__(self,instance,owner)
object.__set__(self,instance,owner)
object__delete__(self,instance)
self指代当前实例,调用者
instance时owner的实例
owner是属性的所属的类
class A:
def __init__(self):
self.a1 = ‘a1‘
print(‘A.init‘)
def __get__(self, instance, owner):
print(‘A.__get__ {} {} {}‘.format(self,instance,owner))
# return self # 返回自身
class B:
x = A()
def __init__(self):
print(‘B.init‘)
self.b = A()
print(‘-‘*20)
print(B.x)
# print(B.x.a1) # 实例返回get的return,如果不写,None没有a1属性
print(‘=‘*20)
b = B()
print(b.b)
B类实例的属性不会触发get方法
Python中,一个类实现了__get__、__set__、__delete__中的任何一个,就是描述器
如果仅实现了__get__,就是非数据描述器
同时实现了__get__、set就是数据描述器
如果一个类的类属性设置为描述器,那么它被成为owner属主
属性的访问顺序
class A:
def __init__(self):
self.a1 = ‘a1‘
print(‘A.init‘)
def __get__(self, instance, owner):
print(‘A.__get__ {} {} {}‘.format(self, instance, owner))
return self
class B:
x = A()
def __init__(self):
print(‘B.init‘)
self.x = ‘b.x‘ # 增加实例属性x
print(‘-‘*20)
print(B.x)
print(B.x.a1)
print(‘=‘*20)
b = B()
print(b.x)
print(b.x.a1) # AttributeError
b是实例,b.x访问实例的属性,而不是描述器
class A:
def __init__(self):
self.a1 = ‘a1‘
print(‘A.init‘)
def __get__(self, instance, owner):
print(‘A.__get__ {} {} {}‘.format(self, instance, owner))
return self
def __set__(self, instance, value):
print(‘A.__set__ {} {} {}‘.format(self, instance, value))
self.data = value
class B:
x = A()
def __init__(self):
print(‘B.init‘)
self.x = ‘b.x‘ # 增加实例属性x
print(B.x)
print(B.x.a1)
b = B()
print(b.x)
print(b.x.a1)
print(B.x.__dict__)
这次B.x.a1返回a1,访问到了描述器的数据
实例的__dict__优先于非数据描述器
数据描述器优先于实例的__dict__
__delete__方法有同样的效果,有了这个方法,就是数据描述器
本质(进阶)
不是数据描述器优先级高,而是数据描述器把实例的属性从__dict__中拿走了,访问顺序没变过
Python中的描述器
练习:
实现staticmethod非数据描述器
class StaticMethod:
def __init__(self, fn):
self._fn = fn
def __get__(self, instance, owner):
return self._fn
class A:
@StaticMethod # stmtd = StaticMethod(stmtd) 属性等于类的实例
def stmtd():
print(‘static method‘)
A.stmtd()
A().stmtd()
实现classmethod非数据描述器
from functools import partial
class ClassMethod:
def __init__(self, fn):
self._fn = fn
def __get__(self, instance, owner):
ret = partial(self._fn, owner)
return ret
class A:
@ClassMethod # clsmtd = ClassMethod(clsmtd)
def clsmtd(cls):
print(cls.__name__)
print(A.__dict__)
A.clsmtd
A.clsmtd()
对实例的数据进行校验
1、
class Person:
def __init__(self, name: str, age: int):
self.params = ((name, str), (age, int))
if not self.checkdata(self.params):
raise TypeError()
self.name = name
self.age = age
def checkdata(self, params):
for p, t in params:
if not isinstance(p, t):
return False
return True
p = Person(‘tom‘, 18)
print(p.checkdata(p.params))
这种代码太过耦合,可以使用装饰器
使用描述器
class Typed:
def __init__(self, name, type):
self.name = name
self.type = type
def __get__(self, instance, owner):
if instance is not None:
return instance.__dict__[self.name]
return self
def __set__(self, instance, value):
if not isinstance(value, self.type):
raise TypeError(value)
instance.__dict__[self.name] = value
class Person:
name = Typed(‘tom‘, str)
age = Typed(18, int)
def __init__(self, name: str, age: int):
self.name = name
self.age = age
p = Person(‘tom‘, 18)
硬编码不好
使用inspect模块
import inspect
class Typed:
def __init__(self, item, type):
self.item = item
self.type = type
def __get__(self, instance, owner):
if instance is not None:
return instance.__dict__[self.item]
return self
def __set__(self, instance, value):
if isinstance(value, self.type):
instance.__dict__[self.item] = value
else:
raise TypeError(value)
def typeassert(cls):
params = inspect.signature(cls).parameters
print(params)
for name, param in params.items():
print(param.name, param.annotation)
if param.annotation != param.empty:
setattr(cls, name, Typed(name, param.annotation))
return cls
@typeassert
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def __repr__(self):
return ‘{} is {}‘.format(self.name, self.age)
__str__ = __repr__
p = Person(‘tom‘, 18)
print(p)
本文出自 “13277682” 博客,谢绝转载!
以上是关于python第九课 面向对象的主要内容,如果未能解决你的问题,请参考以下文章