mro() 调用方法,类的名字加上.mro() # 可以得到查找循序 例子:
class Myname: def __init__(self, nam): self.nam = nam # __name__和__class__的运用 def show(self): # print('11', self.__class__.__name__) # __name__ 不能直接通过实例调用 print('44', __class__.__name__) # 少了self实例得到的是类的名字 def show2(self): print('22', self.__module__) # 类所定义的模块名 # print('55', self.__mro__) class Car(Myname): def carshow(self): print('33', self.__module__) # 类所定义的模块名 b = Car('tom') print(b.show()) print(b.show2()) print(b.carshow()) print(Car.mro() # 查找循序用法: dir() 括号里加上 你想查询的对象 如何用dir()函数查看模块属性和方法,例子:
class Animal: x = 123 def __init__(self, name, age=20, color=10): self.name = name self.age = age self.color = color def show(self): pass print('the dir is {}'.format(dir())) # 在模块中返回模块的属性 print('the dir is {}'.format(dir(Animal))) # 在返回模块的变量名 print(dir([])) class Person: def show(self): a = 100 t = int(a) print(dir()) def test(a=50, b=100): print(dir()) Person().show() # 实例的输出:a t self test() # 只输出变量_new_ 方法很少使用,即使创建了该方法,也会使用 return super().__new__(cls) 基类object的 _new_ 方 法来创建实例并返回。
class Persron(object): def __new__(cls, *args, **kwargs): print('new') return super().__new__(cls) # def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return '<Preson {} {} >'.format(self.name, self.age) if __name__ == '__main__': pi = Persron('tom', 18) print(pi) # 缺少str 或者repe 看到的只是一个类对象的实例 <__main__.Persron object at 0x10214fcf8>例子:
class C: def __init__(self, name): self.name = name def __bytes__(self): # 二进制转换 # return "{} is {}".format(self.name, self.age).encode() import json return json.dumps(self.__dict__).encode() def __repr__(self): # 装换成字符串输出,没有定义的话,会返回对象的内存地址信息 return '<re:{}>'.format(self.name) print(C('tom')) print(bytes(C('123'))) # 二进制装换,成jsonset() 集合的内容和****hash值地址一样就去重。有hash冲突并不去重,也不看id地址
练习: 设置一个二维坐标Point 它是可hash类型,并比较2个实例坐标是否相等
from collections import Hashable # 导入hashable判断是否hash类型 class Point: def __init__(self, x, y): self.x = x self.y = y def __hash__(self): # return hash((self.x, self.y)) return 1 def __eq__(self, other): return self.x == other.x and self.y == other.y a = Point(4, 5) b = Point(5, 6) c = Point(5, 6) print(a.__hash__(), b.__hash__()) print(a == b) print(b == c) print(isinstance(a,Hashable)) #True例子:(注意不能通过是否带引号来判断输出值的类型,判断类型还是要选择type或者isinstance)
class B: def __bool__(self): return False print(bool(B)) # 判断有没有存在 print(bool(B())) # 有定义的实例调用为return class A: pass class C: def __len__(self): return 0 print(bool(A())) if A(): print('Real A') print(bool(A)) print(bool(A())) # 没有定义,全部实例返回为真 print(bool(C())) # __bool__没找到默认找__len() 返回长度,非0为真往往是用面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式。例如,上例中 的对+进行了运算符重载,实现了Point类的二元操作,重新定义为Point + Point。 提供运算符重载,比直接提供加法方法要更加适合该领域内使用者的习惯。
例子:实现A类的2个实例相减
class A: def __init__(self, name, age): self.name = name self.age = age def __sub__(self, other): # 定义的是- return self.age - other.age def __isub__(self, other): # 如果没有定义__isub__,则会调用__sub__ return A(self.name, self - other) a = A('kek', 18) b = A('yuyu', 20) print(a - b)例子二:我们定义一个“人”的类People,当中有属性姓名name、年龄age。让你需要利用sorted函数对一个People的数组进行排序,排序规则是按照name和age同时排序,即name不同时比较name,相同时比较age。由于People类本身不具有比较功能,所以需要自定义,你可以这么定义People类
class People: def __init__(self, name, age): self.name = name self.age = age def __gt__(self, other): return self.name > other.name if self.name != other.name else self.age > other.age def __repr__(self): return '<{}: {} {}>'.format(__class__.__name__, self.name, self.age) tom = People('tom', 18) jenny = People('jenny', 20) s1 = [1, 4, 2, 5, 123, 3] for item in sorted([tom, jenny, People('jenny', 28)]): print(item)__eq__ 等于可以推断不等于 __gt__ 大于可以推断小于 __ge__ 大于等于可以推断小于等于 也就是用3个方法,就可以把所有比较解决了
例子:构建一个购物车
class Cart: # 初始化列表 def __init__(self): self.car = [] # 调用函数,增加 def additem(self, name): return self.car.append(name) # 不要返回对象内存地址 def __repr__(self): return '<{} {}>'.format(__class__.__name__, self.car) # 判断列表长度 def __len__(self): return len(self.car) # 可以迭代 def __iter__(self): return iter(self.car) # 返回一个对象可以链式编程 def __add__(self, other): self.car.append(other) return self # 通过索引取到列表的值 def __getitem__(self, index): return self.car[index] # 通过索引修改列表的值 def __setitem__(self, index, value): self.car[index] = value # return self # def __contains__(self, index): # self.car = index # def # def __add__(self, other): cart = Cart() cart.additem(1) cart.additem('abc') cart.additem(3) print(cart.car) # 长度、bool print(len(cart)) print(bool(cart)) # # 迭代 for x in cart: print(x, end=' ') # # in print() print(3 in cart) print(2 in cart) # # 链式编程实现加法 print(cart + 4 + 5 + 6) print(cart.__add__(17).__add__(18)) # 索引操作 print(cart[1]) cart[1] = 'xyz'python 中一切皆对象 __call__,类中定义一个该方法,实例可以像函数一样使用 例子:
def foo(): print(foo.__module__, foo.__name__) # 类的名字,函数的名字 foo() foo.__call__() # 函数即对象,对象foo加上() ,就是调用函数对象call__() 方法练习: 定义一个类,并实例化得到其实例,像实例函数一样去调用。
class Person: def __init__(self, name, age): self.name = name self.age = age def __call__(self, *args, **kwargs): return self def __repr__(self): return '<{},{},{}>'.format(self.__class__.__name__, self.name, self.age) c1 = Person('tom', 12) print(c1()) class add: ## 不初始化对象 # def __call__(self, *args, **kwargs): # ret = 0 # for x in args: # print(x) # ret += x # self.ret = ret # return ret # 初始化对象 def __init__(self, *args): self.args = args def __call__(self, *args, **kwargs): ret = 0 for i in args: ret += i return ret adder = add() print(adder(4, 6)) # print(adder.ret) ## 2. 定义一个斐波那契数列,计算第n项[0、1、1、2、3、5、8、13、21] # [0、1、1、2、3、5、8、13、21] # 普通方法 a = 1 b = 0 for i in range(6): a, b = b, a + b print(b) # 函数,生成器 做成生成器函数 def fib(): a = 1 b = 0 while True: a, b = b, a + b yield a foo = fib() for i in range(10): print(next(foo)) # 递归 def fib(n):return n if n <= 1 else fib(n - 2) + fib(n - 1) #做成一个类: class Fib: def __init__(self): self.x = 1 self.y = 1 def __call__(self, index): if index <= 1: return index else: for i in range(index - 2): self.x, self.y = self.y, self.x + self.y return self.y fib = Fib() print(fib(5)) # 如何提高效率,一是使用cache缓存,二是将类做成一个列表, # 一 缓存 import functools @functools.lru_cache() def fib(n): return n if n <= 1 else fib(n - 2) + fib(n - 1) print(fib(135)) # 二 类的列表 实现,长度,可索引,可迭代,可实例调用 class Fib: def __init__(self): self.lb = [0, 1, 1] def __call__(self, index): if index < len(self.lb): return self.lb[index] for i in range(3, index+1): self.lb.append(self.lb[i - 2] + self.lb[i - 1]) return self.lb fib = Fib() print(fib(3)) class Fib: def __init__(self): self.arrlis = [0, 1, 1] def __call__(self, index): if index < len(self.arrlis): return self.arrlis[index] for i in range(3, index + 1): self.arrlis.append(self.arrlis[i - 2] + self.arrlis[i - 1]) return self.arrlis[index] # 可迭代 def __iter__(self): return iter(self.arrlis) #返回长度 def __len__(self): return len(self.arrlis) #通过索引取值 def __getitem__(self, item): return self.arrlis[item] fib = Fib() print(fib(5)) for i in fib: print(i) print(len(fib)) print(fib[4])使用with…as语法,可以用文件IO操作对文件对象使用上下文管理当一个对象同时实现__enter__ 和__exit__的方法,它就属于上下文管理。enter 和__exit__必须成对出现
方法意义__enter__进入对象相关的上下文,with语法会把其返回值绑定到 as 字句的指定变量上__exit__退出上下文管理,返回值可以压制主程序的异常例子:
class Point: def __init__(self): print('1') def __enter__(self): print('2') return '===' def __exit__(self, exc_type, exc_val, exc_tb): print('3') if __name__ == '__main__': with Point() as f: # 实例化对象时,会执行init属性 但是不会进入enter里,with语法会调用到enter方法。 print('---') print(f) # ==enter里的返回值注意:with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。with并不开启一个新的作用域。
异常并不会影响上下文的进入和退出。with会调用enter方法。
即以上例得到,如果调用上下文管理,先会初始化实例,然后通过with 执行enter方法,里面执行完之后再执行主语句体的内容。如果主语句体报错,也不会影响exit的执行
所以:上下文管理是安全的
__enter__ 方法 没有其他参数。 __exit__ 方法有3个参数: __exit__(self, exc_type, exc_value, traceback) return True 可以压制异常 这三个参数都与异常有关。 如果该上下文退出时没有异常,这3个参数都为None exc_type ,异常类型 exc_value ,异常的值 traceback ,异常的追踪信息
练习:为加法函数计时 1.用装饰器显示函数的执行时长 2 . 用上下文管理方法来显示函数的执行时长
##1 import time import datetime from functools import wraps def logger(fn): @wraps(fn) def inner(*args, **kwargs): print('begin') start = datetime.datetime.now() ret = fn(*args, **kwargs) end = ((datetime.datetime.now()) - start).total_seconds() print('{},run is {}'.format(fn.__name__, end)) return ret return inner @logger # add = logger(add) def add(x, y): time.sleep(2) return x + y if __name__ == '__main__': print(add(4, 5)) ## 2 import time import datetime from functools import wraps class Timeni: def __init__(self, fn): self.fn = fn def __enter__(self): print('begin') self.start = datetime.datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): end = (datetime.datetime.now() - self.start).total_seconds() print('{},run time is {}'.format(self.fn.__name__, end)) print('byebye') def add(x, y): time.sleep(2) return x + y if __name__ == '__main__': with Timeni(add) as f: print(add(4, 5)) print(add(7, 8)) # 或者可以将Timeni当成可调用对象实现,就不用写add def __call__(self, x, y): return self.fn(x, y) if __name__ == '__main__': with Timeni(add) as f: print(f(4, 5))修改成类属性装饰器:
import time import datetime from functools import wraps class Timeni: """this is timeni class""" def __init__(self, fn): self.fn = fn def __call__(self, *args, **kwargs): self.start = datetime.datetime.now() ret = self.fn(*args, **kwargs) duration = (datetime.datetime.now() - self.start).total_seconds() print('{} run is {}'.format(self.fn.__name__, duration)) return ret @Timeni # add = Timeni(add) def add(x, y): """this is add function""" time.sleep(2) return x + y if __name__ == '__main__': print(add(4, 5)) print(add.__doc__) # doc的名字变了 print(add.__class__.__name__) # add 的名字是类的名字 # 修改: ## 在类的init方法下直接修改字符串self.__doc__ = self.fn.__doc__ ##方法二: from functools import wraps, update_wrapper 在__init__下,调用update_wrapper(self,fn) ## 方法三: wrap(fn)(self)总结上下文应用场景
可以增强功能打开的资源可以自动关闭,例如文件对象,网络连接,数据库连接。权限验证(在执行代码前,做权限验证。在__enter__中处理。)它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现 __enter__ 和 __exit__ 方法。 对下面的函数有要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值。 也就是这个装饰器接收一个生成器对象作为参数
contextlib.contextmanager装饰器,yield前代表__extend__ 。yield后代表__exit__ yield代表__extend的返回值
import contextlib @contextlib.contextmanager def add(): """this is add function""" print('begin') try: yield 2 finally: print('end') if __name__ == '__main__': with add() as f: print(f) # 如果不加 try..finally, 程序出现异常时,无法执行后继的end,练习:依然是给add函数通过上下文管理的方式计算时间
import contextlib import time import datetime @contextlib.contextmanager def add(x, y): """this is add function""" print('begin') start = datetime.datetime.now() try: yield x + y finally: duration = (datetime.datetime.now() - start).total_seconds() print('the time is {}'.format(duration)) if __name__ == '__main__': with add(4, 5) as f: time.sleep(2) print(f)总结 如果业务逻辑简单可以使用函数加contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加__enter__ 和 __exit__ 方法方便。
反射,reflection,指的是运行时获取类型定义信息 简单说,在Python中,在运行的时候,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或者自省。 具有反射能力的函数有type(),isinstance(),callable(),dir(), getattr()
内建函数意义getattr(object, name[, default])通过name返回object的属性值。当属性不存在,将使用default返回,如果没有 default,则抛出AttributeError。name必须为字符串setattr(object, name, value)object的属性存在,则覆盖,不存在,新增hasattr(object, name)判断对象是否有这个名字的属性,name必须为字符串如果为实例增加方法,是没有绑定的 例子:有一个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(4, 5) print(p) # p.show() setattr(p, 'z', 21) print(getattr(p, 'z', 'NONe')) print(getattr(p, 'k', 'NONe')) print(getattr(p, '__dict__')) print(dir(p)) print(p.__dir__()) # 动态调用方法 if hasattr(p, 'show'): getattr(p, 'show')() # 为类创建相等 setattr(Point, 'xyz', 100) print(Point.__dict__) # 为类增加方法 if not hasattr(Point, 'show'): setattr(Point, 'show', lambda self: print(self.x, self.y)) p2 = Point(4, 5) p2.show() # 实现两个实例相加 if not hasattr(Point, 'add'): setattr(Point, 'add', lambda self, other: Point(self.x + other.x, self.y + other.y)) print(p2.add(p)) # 实现两个实例相减 if not hasattr(Point, 'sub'): setattr(Point, 'sub', lambda self, other: Point(self.x - other.x, self.y - other.y)) print(p2.sub(p))练习:建立一个命令分发器,通过名称找对应的函数执行
class Fenfa: def __init__(self): pass def reg(self, name, fn): setattr(self, name, fn) def run(self): while True: cmd = input('>>>') if cmd == 'quit': print('bye') break getattr(self, cmd, lambda : print('Not emted{}'.format(cmd)))() fenfa = Fenfa() fenfa.reg('ls', lambda :print('so')) fenfa.run()__getattr__的使用例子:
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的话只能返回None # return "missing{}".format(item) value = 1000 self.__dict__[item] = value # 所有缺失属性的一个缺省值,可以加到实例字典看到,相当于后悔药,如果直接return 1000 加不进实例字典里 return value P1 = Point(4, 5) print(P1.x) print(P1.z) print(P1.n) print(P1.t) # 跟getattr 相关,没有getattr的话会报错,表示找不到属性 print(P1.l) print(P1.__dict__)__setter__使用方法:实例通过.点号设置属性,例如 self.x = x 属性赋值,就会调用 __setattr__() ,属性要加到实例的 __dict__ 中,就需要自己完成。
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的话只能返回None # return "missing{}".format(item) value = 1000 self.__dict__[item] = value return value def __setattr__(self, key, value): self.__dict__[key] = value # print('{} = {}'.format(key, value)) P1 = Point(4, 5) print(P1.x) print(P1.y) print(P1.__dict__) P1.z = 250 # 实例通过.点号设置属性,例如 self.x = x 属性赋值,就会调用 __setattr__() ,属性要加到实例的 __dict__ 中,就需要自己完成。 print(P1.z) print(P1.__dict__)__delattr__() 使用方法:防止实例删除属性
class Base: n = 0 class Point(Base): z = 6 # d = {'tttt': '20000'} def __init__(self, x, y): self.x = x self.y = y # def __delattr__(self, item): # print('is not del {}'.format(item)) # return super().__delattr__(self, *args) P1 = Point(4, 5) del P1.x # 可以删除 print(P1.__dict__) print(Point.__dict__) # P1.z = 15__getattribute__和__getattr配合使用。__getattribute__实例所有的属性调用都从这个方法开始
class Point(Base): z = 6 d = {'tttt': '20000'} def __init__(self, x, y): self.x = x self.y = y def show(self): print(self.x, self.y) def __getattr__(self, item): return __class__.d[item] # def __setattr__(self, key, value): # self.__dict__[key] = value # # print('{} = {}'.format(key, value)) def __getattribute__(self, item): # return super.__getattribute__(item) raise AttributeError('aaa') P1 = Point(4, 5) print(P1.tttt)用到3个魔术方法: __get__() 、 __set__() 、 __delete__() 方法签名如下
object.__get__(self, instance, owner)object.__set__(self, instance, value)object.__delete__(self, instance) self 指代当前实例,调用者 instance 是owner的实例 owner 是属性的所属的类 class A: def __init__(self): self.a1 = 'a1' print('A init') # A的实例 #属组的实例 # 属组的类 def __get__(self, instance, owner): # 需要返回值,instance属组的属性即B实例, owner print(self, instance, owner) print(self.__dict__) return self class B: # 属组 x = A() # 访问A实例里面的get def __init__(self): print('b init') # print(B.x) # 对A实例的魔术方法的get # print(B.x.a1) # A的实例访问a1 b = B() print(b.x) #因为定义了 __get__ 方法,类A就是一个描述器,对类B或者类B的实例的x属性读取,成为对类A的实例的访问, 就会调用 __get__ 方法 class A: def __init__(self): self.a1 = 'a1' print('A init') # A的实例 #属组的实例 # 属组的类 # 描述器 def __get__(self, instance, owner): # 需要返回值,instance属组的属性即B实例, owner print(self, instance, owner) print(self.__dict__) return self class B: # 属组 x = A() # 类属性可以,描述器和属主类的类属性有关 def __init__(self): self.y = A() # 实例属性没用 print('b init') b = B() print(b.y) # b.x才会触发描述器的方法Python中,一个类实现了 __get__ 、 __set__ 、 __delete__ 三个方法中的任何一个方法,就是描述器。 如果仅实现了 __get__ ,就是非数据描述符 non-data descriptor; 同时实现了 __get__ 、 __set__ 就是==数据描述符 data descriptor ==赋值将会跑到set方法当中。优先级更高 如果一个类的类属性设置为描述器实例,那么它被称为owner属主。 当该类的属性被查找、设置、删除的时候,就会调用描述器相应的方法。
练习:实现staticmethod装饰器、实现classMethod装饰器
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') A.stmtd() A().stmtd() from functools import partial # 偏函数 class ClassMethod: def __init__(self, fn): self.fn = fn def __get__(self, instance, cls): ret = partial(self.fn, cls) # 将例注入 return ret class B: @ClassMethod # stmtd = StaticMethod(stmtd) def stmt(cls, x, y): print(cls.__name__, x, y) print(B.__dict__) print(B.stmt) B.stmt(4, 5) B().stmt(4, 5) 实现property装饰器: #!/usr/bin/env python # -*- coding: utf-8 -*- class Property: def __init__(self, fget, fset=None): # 等于一个函数,实例调用 self.fget = fget self.fset = fset def __get__(self, instance, owner): return self.fget(instance) def __set__(self, instance, value): # 先实现只读属性 if self.fset is None: raise AttributeError(123) self.fset(instance, value) # 返回 def setter(self, fn): self.fset = fn return self class A: def __init__(self, x): self._x = x @Property # self.fn = Property(self.fn) def x(self): return self._x @x.setter def x(self, value): self._x = value a = A(10) print(a.x) a.x = 1000 print(a.x) 对实例的数据进行校验,判断,函数先写 class Person: def __init__(self, name: str, age: int): parmr = ((name, str), (age, int)) if not self.chank(parmr): raise TypeError('int or str') self.name = name self.age = age def chank(self, parmr): for i,v in parmr: if not isinstance(i,v): return False return True Person('tom','12')对实例的数据进行校验,装饰器
import inspect def check(cls): def wrapper(*args, **kwargs): sig = inspect.signature(cls) # 拿到签名 params = sig.parameters # 拿到有序字典 # print(params.items()) for x, (k, v) in zip(args, params.items()): if v.annotation != inspect._empty and not isinstance(x, v.annotation): raise TypeError('1!!!!!!!!!!!!!') re = cls(args, kwargs) print('end') return re return wrapper @check # Person = check(Pecson) class Person: def __init__(self, name: str, age: int): self.name = name self.age = age a = Person('tom', '12') # 报错,成功对实例的数据进行校验,描述器
class TypeCheck: # 描述器 def __init__(self, name, typ): self.name = name self.type = typ def __get__(self, instance, owner): # print('get~~~~~~~~~~~~~~') if instance: return instance.__dict__[self.name] else: raise Exception # 或者return self,总之不正常 def __set__(self, instance, value): # print('set~~~~~~~~~~~~~') if instance: if not isinstance(value, self.type): raise TypeError(self.name, '+++++++++++') else: instance.__dict__[self.name] = value # 存回到属主类的实例字典中,默默的检查,不出错的话,像什么都没有发生一样 # def __set_name__(self, owner, name): # python3.6新增的方法 # print(name) # self.name = name class TypeInject: def __init__(self, cls): self.cls = cls def __call__(self, *args, **kwarg): sig = inspect.signature(self.cls) params = sig.parameters # print(params) # OrderedDict([('name', <Parameter "name:str">), ('age', <Parameter "age:int">)]) for name, param in params.items(): # print(name, param.annotation) if param.annotation != param.empty: # inspect._empty setattr(self.cls, name, TypeCheck(name, param.annotation)) return self.cls(*args, **kwarg) @TypeInject # Person = TypeInject(Person) class Person: def __init__(self, name: str, age: int): self.name = name self.age = age p5 = Person('Green', 28)