python面对对象计算矩形_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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值