python基础之魔法方法
由于hexo自带的markdown渲染引擎对双下划线做了转义,在正文中看到的魔法方法前后都没有双下划线
setattr、getattr、delattr
可以拦截对对象属性的访问
setattr函数是用来设置对象的属性,通过object中的setattr函数来设置属性
- class Ceshi:
- def __init__(self,n):
- self.name=n
- def __setattr__(self,key,value):
- print("------setattr-----key:%s---value:%s"%(key,value))
- c=Ceshi("wys")#------setattr-----key:name---value:wys
- c.age=12#------setattr-----key:age---value:12
- --------------------------------------------------
- print(c.name)#报错 AttributeError: 'Ceshi' object has no attribute 'name'
在c对象被实例化的时候,触发了__serattr__函数。在c.age赋值操作的时候,也触发了__setattr__函数,说明每次只要给属性设置的时候,都会触发这个函数 但是在打印c.name的时候却被告知没有name这个属性。是因为我们在这里只是打印了值,并没有去修改或者保存值。这里不能用self.key=value,因为key是一个字符串,对象.‘属性名’的调用方式是行不通的,必须是对象.属性名。也不能setattr(self,key,value),setattr就是在设置对象的属性的值,一旦设置属性的值就回触发__setattr__函数,所以会无限递归下去,应该使用self.__dict__[key]=value。在__setattr__中我们还可以添加对传入的值的类型控制等满足程序自定义的操作
- class Ceshi:
- def __init__(self,n):
- self.name=n
- def __setattr__(self,key,value):
- if not isinstance(value,int):#只允许数字类型
- raise TypeError("只能输入整形")
- self.__dict__[key]=value
- c=Ceshi("wys")#------setattr-----key:name---value:wys
- c.age=12#------setattr-----key:age---value:12
- print(c.name)
- ----------以下为输出-------------
- ------setattr-----key:name---value:yangzai
- ------setattr-----key:age---value:12
- wys
delattr用来删除对象的属性
- class Ceshi:
- def __init__(self,n):
- self.name=n
- def __setattr__(self,key,value):
- self.__dict__[key]=value
- def __delattr__(self,item):
- print("delattr-----%s"%item)
- c=Ceshi("wys")#------setattr-----key:name---value:wys
- c.age=12#------setattr-----key:age---value:12
- del c.age#delattr-----age
- print(c.age)#
当在删除对象的属性的时候,会触发__delattr__函数,这里跟__setattr__函数的触发机制是一样的,在这里要做删除对象的属性操作,注意,不能写del self.item,也不能使用delattr()函数,回形成无限递归。正确姿势应该是self.__dict__.pop(item)。同理,我们也可以在__delattr__中添加一些特殊的控制
- class Ceshi:
- def __init__(self,n):
- self.name=n
- def __setattr__(self,key,value):
- self.__dict__[key]=value
- def __delattr__(self,item):
- self.__dict__.pop(item)
- c=Ceshi("wys")#------setattr-----key:name---value:wys
- c.age=12#------setattr-----key:age---value:12
- del c.age#delattr-----age
- print(c.age)#
getattr当在对象.dict找不到的属性时候,就会触发这个函数
- class A:
- def __init__(self,name):
- self.name=name
- def __getattr__(self,item):
- print("--getattr---%s"%item)
- a=A("wys")
- a.name#到着一步也没有__getattr__函数
- a.age#打印 --getattr---age,属性不存在的时候触发了__getattr__
__getattr__函数可以来定制自定义数据类型
getitem setitem delitem
getitem(self,key):定义获取容器中指定元素的行为,相当于 self[key]
setitem(self,key,value):定义设置容器中指定元素的行为,相当于 self[key] = value
delitem(self,key):定义删除容器中指定元素的行为,相当于 del self[key]
- class Mydict:
- def __init__(self,name):
- self.name=name
- def __getitem__(self,item):
- print("--getitem--")
- return self.__dict__[item]
- def __setitem__(self,key,value):
- print("--setitem--")
- self.__dict__[key]=value
- def __delitem__(self,key):
- print("--delitem---")
- self.__dict__.pop(key)
- def __setattr__(self, item, value):
- print("--setattr--")
- self.__dict__[item] = value
- def __delattr__(self, item):
- print("--delattr--")
- return self.__dict__.pop(item)
- # def __delattr__(self,item):
- # 'del self.item时,触发该方法'
- f=Mydict("yangzai")#触发了__setattr__方法
- f["myclass"]="3b"#触发了__setitem__方法
- print(f["myclass"])#触发了__getitem__方法
- f["age"]=18#触发__setitem__方法
- del f["age"]#触发__delitem__方法
- print(f.myclass)#触发了__getattr__方法
- print(f.name)#触发__getattr__
- f.name="wys2"#触发__setattr__方法
- print(f.__dict__)#{'age': 18, 'name': 'wys2'}
- del f.age#触发__delattr__方法
doc
表示对象的描述信息。不能被继承
- class A:
- '着是描述信息'
- def ceshi(self):
- 'this is a ceshi func'
- pass
- print(A.__doc__)#---------->着是描述信息
- a=A()
- print(a.ceshi.__doc__)#----->this is a ceshi func
slots
限制实例的属性,是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)。slots属性就规定了,实例化后的对象只能有固定的一些属性,不会再创建命令空间,这样的好处是可以节省内容空间。这样的设计常用于那些属性是固定的,并且要实例化大量的对象的场景
- class A:
- __slots__=["x","y"]
- a=A()
- a.x=10
- a.y=12
- a.z=232#报错,AttributeError: 'A' object has no attribute 'z'
- print(a.y)#---->12
- print(a.x)#---->10
- print(a.__dict__)#报错,AttributeError: 'A' object has no attribute '__dict__'
- print(A.__dict__)#--------->{'__doc__': None, 'x': <member 'x' of 'A' objects>, '__slots__': ['x', 'y'], 'y': <member 'y' of 'A' objects>, '__module__': '__main__'}
next和iter实现迭代器协议
有iter方法的是可迭代对象iterable(如list、tuple、dict、set、str等),执行iter方法就得到了一个迭代器iterator(包括生成器和带yield的generator function)。迭代器里有next和iter方法。可使用isinstance(f,Iterator)判断对象是否是迭代器。迭代器的特点是next一次才拿一次值,同一时刻在内存里只有一次值,节省了内存空间。缺点是不next到最后,无法知道还有多少值,同时只能往后走,不能往前走,这个过程是一次性的,next完就完成了。
- class Foo:
- def __init__(self,st,end,buc=1):#模拟range的功能
- self.start=st
- self.end=end
- self.buc=buc
- def __iter__(self):
- return self#迭代器__iter__方法得到自己本身
- def __next__(self):
- if self.start>self.end-1:#模拟超出next范围值之后就抛出StopIteration
- raise StopIteration
- n=self.start
- self.start+=self.buc
- return n
- f=Foo(1,20,5)
- # print(next(f))
- # print(next(f))
- # print(next(f))
- for i in f:#for可自动捕捉异常
- print(i)
- from collections import Iterable,Iterator
- print(isinstance(f,Iterator))
- print(isinstance(f,Iterable))
析构函数del
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
- class Foo:
- def __init__(self,file,mode="r",encode="utf8"):
- self.f=open(file,mode=mode,encoding=encode)
- def __getattr__(self,item):
- print("---getattr--")
- return getattr(self.f,item)
- def __del__(self):
- print("__del__")
- self.close()#会触发__getattr__方法
- f=Foo("a.txt")
- del f #解除f跟值的绑定关系,触发析构函数
- --------以下为输出---------
- ------------------>
- __del__#即使不del f,在文件执行完成后,值的引用计数为0,执行__del__函数
- ---getattr--
另外一种情况:
- f=Foo("a.txt")
- f2=f
- del f#并不会触发__del__函数,因为还有f2保持对值Foo("a.txt")的引用
- print("-------------->")
或者是:
- f=Foo("a.txt")
- #在执行完上述操作后,仍然会执行__del__,因为python的解释器会定期回收垃圾
上下文管理协议 enter 和exit
- with open("a.txt",encoding="utf8",mode="r")
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enter和exit方法
- import time
- class Open:
- def __init__(self,filpath,mode="w+",encode="utf8"):
- self.path=filpath
- self.x=open(filpath,mode=mode,encoding=encode)#文件句柄
- def write(self,value):
- t=time.strftime("%Y-%m-%d")
- self.x.write(t+value)#操作文件句柄,然后自定义要写入的内容
- def __getattr__(self,item):#当找不到属性read,seek,flush等属性的时候,触发
- #self.x.read 错误
- if hasattr(self.x,item):
- return getattr(self.x,item)
- def __enter__(self):
- print("__enter__")
- return self
- def __exit__(self, exc_type, exc_val, exc_tb):
- 'exc_type:异常类型。exc_val:异常信息。exc_tb:异常的追踪信息'
- print("__exit__")
- self.close()#在这里关闭文件
- return True #代表已经处理结果了(程序就不会抛异常)
- def __del__(self):
- print("--del--")
- self.close()
- with Open("a.txt"):
- print("可以不用as f,语法上没有问题")
- with Open("a.txt") as f:#实际上是把Open("a.txt").__enter__()赋值给f
- f.write("")
- raise TypeError("error")
- -------以下为输出---------
- __enter__#先执行__enter__方法,拿到实例化后的对象,并赋值给f
- 23232#再执行with代码体,执行终止后(如果发生异常也终止),就去执行__exit__,如果是异常终止,会将异常传给__exit__方法
- __exit__#最后执行__exit__方法
call
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
- class Peopel:
- pass
- peo=Peopel()#只所有可以加()运行,是因为元类type里有__call__方法
- peo()#报错,对象peo会首先去找__call__方法,Peopel类中没有__call__绑定方法
- class Peopel:
- def __call__(self):
- print("call")
- print(callable(Peopel))#-------->True
- peo=Peopel()
- print(callable(peo))#--------->True
- peo()#完美运行
元类
创建类三点要素:类名,类的命名空间,类的继承。可通过type()方式手动创建一个类。type是元类,是类的类
创建一个类:type(类名:"ceshi",类的继承:(),类的命令空间:{})。
- ce=type("ceshi",(),{})
- print(ce)#-----》<class '__main__.ceshi'>
- print(ce.__bases__)#默认会有----->(<class 'object'>,)
- print(ce.__dict__)#会有一些默认饿属性---->{'__doc__': None, '__dict__': <attribute '__dict__' of 'ceshi' objects>, '__weakref__': <attribute '__weakref__' of 'ceshi' objects>, '__module__': '__main__'}
- class ceshi:
- pass
- #上边两种创建方式是一样的
- #手动创建一个=自定义属性的类
- x=1
- def run(self):
- print("run")
- class_name="myclass"
- class_bases=(object,)
- class_dict={
- "x":1,
- "run":run
- }
- mc=type(class_name,class_bases,class_dict)#创建了一个对象--》类
- print(type(mc))#---------><class 'type'>
- print(mc.__dict__)#-------->{'__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__dict__': <attribute '__dict__' of 'myclass' objects>, '__doc__': None, 'x': 1, 'run': <function run at 0x0000000000B507B8>, '__module__': '__main__'}
自定义类的产生,需要自定义一个元类,这个元类要去继承元类type,在自定义的类中,去设置元类为自定义的元类。然后就可以通过自定义的元类去控制类的产生
- class Mymeta(type):
- pass
- #Mymeta("Foo",(object,),{"x":1,"run":run})
- class Foo(metaclass=Mymeta):#默认情况下,metaclass=type
- x=1
- def __init__(self):
- pass
- def run(self):
- print("running")
上边定义的class Foo就相当于Mymeta("Foo",(object,),{"x":1,"run":run}),Mymeta也是一个类,类加括号是在实例化一个对象,首先会将括号里的参数传给Mymeta的init方法,也就是在在class一个类的时候,会去触发元类里的实例化方法init(self,classname,classbases,classdic),现在这个self就是类Foo。现在要去创建这个Foo类,就要去自定义元类Mymeta的父类type里边去执行init(self,classname,classbases,classdic)方法。在Mymeta的init可以去控制类
- class Mymeta(type):#自定义元类,继承元类type
- def __init__(self,class_name,class_bases,class_dic):
- for key in class_dic:#检测如果类里函数没有写doc注释,就抛异常
- if not callable(class_dic[key]):continue
- if not class_dic[key].__doc__:
- raise TypeError("the func or bound method must be hava doc")
- #type.__init__(self,class_name,class_bases,class_dic)#到元类里去初始化,这里如果不写这句代码,也会默认去调用元类type的__init__方法
- def __call__(self,*args,**kwargs):#类Foo要加括号()运行,就是因为元类中有这个方法。流程如下:
- 'self:Foo'
- obj=self.__new__(self)#用Foo下面的一个__new__方法产生一个空对象,obj为空
- self.__init__(obj,*args,**kwargs)#然后Foo再掉__init__方法,将空对象和参数传入,进行初始化操作
- return obj#一定要返回这个初始化后的对象否则返回的为None
- #Mymeta("Foo",(object,),{"x":1,"run":run})
- class Foo(metaclass=Mymeta):#默认情况下,metaclass=type
- x=1
- def __init__(self,name):
- 'init obj'
- self.name=name#obj.name=name
- pass
- def run(self):
- 'run'
- print("running")
- f=Foo("wys")
- print(f.name)
元类是深度的魔法,一般在研究大型框架或者超大型项目时才用得的到,而且对元类的使用也会非常的明确。
python基础之魔法方法的更多相关文章
- python类之魔法方法
python类之魔法方法: class A(object): def __init__(self,x): self.x = x def __neg__(self): print('-v') def _ ...
- python里的魔法方法1(构造与析构)
魔法方法——构造与析构 1.python编程的魔法方法: (1)魔法方法总是被双下划线包围,例如__init__: (2)魔法方法是面向对象的python的一切. 2.__new__(class[,… ...
- Python学习8——魔法方法、特性和迭代器
Python中很多名称比较古怪,开头和结尾都是两个下划线.这样的拼写表示名称有特殊意义,因此绝不要在程序中创建这样的名称.这样的名称中大部分都是魔法(方法)的名称.如果你的对象实现了这些方法,他们将在 ...
- Python中的魔法方法
1.什么是魔法方法? 魔法方法就是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而这一 ...
- Python 入门之Python基础数据类型及其方法
Python 入门之Python基础数据类型 1. 整型:int 用于计算,用于比较 (在赋值的时候先执行等号右边的内容) 1.1 整数的加 a = 10 b = 20 print(a + b) 结果 ...
- python基础函数、方法
python的函数和方法,通过def 定义: 函数的特性: 减少重复代码 使程序变的可扩展 使程序变得易维护 函数和方法的区别:函数有返回值.方法没有 语法定义: def sayhi():#函数名 p ...
- python中类的魔法方法
__xx__这种方法,在Python中均称为魔法方法 1.__init__(self) 该方法的作用是初始化对象 在创建对象时被默认调用,不需要手动调节 self参数不需要开发者传递,解释器会自动将创 ...
- python的常用魔法方法详细总结
构造和初始化 __init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数". 实际上, 当我们调用x = SomeClass()的时候调用,__init_ ...
- Python基础之公共方法
公共方法:就是列表,元组,字典,字符串能共同使用的方法: Python内置函数 内置函数罗列 函数 描述 备注 len(item) 计算容器中的元素个数 del(item) 删除变量 del有两种方法 ...
随机推荐
- 前台时间格式 2019-03-09T16:00:00.000Z
问题描述: 本想在前台把字符串格式的日期(2019-03-09)转换成日期格式(2019-03-09 00:00:00),但当把这个参数传到后台去后却变成了2019-03-08T16:00:00.00 ...
- AirtestIDE实践二:Poco框架试用
上一篇用airtest框架做了一个梦幻西游手游的DEMO,这次看看poco的强大之处.首先安装poco:pip install pocoui 其次,把SDK集成到你家游戏中,我这直接用官网提供的一个U ...
- 初学Direct X(9) ——文字的显示
初学Direct X(9) --文字的显示 本次学习如何使用ID3DXFont创建字体,使得我们可以在任何安装了Windows系统中TrueType字体来打印文字,不过最好使用标准字体,这样文字在每一 ...
- leetcode-回文链表
请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true 进阶:你能否用 O(n) 时间复杂 ...
- 【转】: 探索Lua5.2内部实现:虚拟机指令(2) MOVE & LOAD
name args desc OP_MOVE A B R(A) := R(B) OP_MOVE用来将寄存器B中的值拷贝到寄存器A中.由于Lua是register based vm,大部分的指令都是直接 ...
- 【Linux 运维】linux系统修改主机名
主机名的修改: 1.命名解释: [root@localhost~]# 分别代表: 用户名(root) 主机名(localhost) 当前路径(~,当前用户的home目录) 权限标志位(#代表root ...
- python leveldb 文档
标签(空格分隔): python leveldb import leveldb db = leveldb.LevelDB('./db') db.Put('hello', 'world') print ...
- WebApi中利用Razor模板引擎来生成html
在服务器端基于Razor来生成html的一个思路 using System.Web.Mvc; using System.IO; using System.Web.Routing; using Syst ...
- java超强分页标签演示
最近在做一个项目,用到了一个分页,于是动手写了个分页标签,先将代码贴出来,供大家交流,写的不好,请见谅!. 以下是java标签类,继承自SimpleTagSupport package com.lyn ...
- jQuery实现仿京东商城图片放大镜
效果图: 不废话直接上代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...