由于hexo自带的markdown渲染引擎对双下划线做了转义,在正文中看到的魔法方法前后都没有双下划线

setattr、getattr、delattr

可以拦截对对象属性的访问

setattr函数是用来设置对象的属性,通过object中的setattr函数来设置属性

  1. class Ceshi:
  2. def __init__(self,n):
  3. self.name=n
  4. def __setattr__(self,key,value):
  5. print("------setattr-----key:%s---value:%s"%(key,value))
  6. c=Ceshi("wys")#------setattr-----key:name---value:wys
  7. c.age=12#------setattr-----key:age---value:12
  8. --------------------------------------------------
  9. 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__中我们还可以添加对传入的值的类型控制等满足程序自定义的操作

  1. class Ceshi:
  2. def __init__(self,n):
  3. self.name=n
  4. def __setattr__(self,key,value):
  5. if not isinstance(value,int):#只允许数字类型
  6. raise TypeError("只能输入整形")
  7. self.__dict__[key]=value
  8. c=Ceshi("wys")#------setattr-----key:name---value:wys
  9. c.age=12#------setattr-----key:age---value:12
  10. print(c.name)
  11. ----------以下为输出-------------
  12. ------setattr-----key:name---value:yangzai
  13. ------setattr-----key:age---value:12
  14. wys

delattr用来删除对象的属性

  1. class Ceshi:
  2. def __init__(self,n):
  3. self.name=n
  4. def __setattr__(self,key,value):
  5. self.__dict__[key]=value
  6. def __delattr__(self,item):
  7. print("delattr-----%s"%item)
  8.  
  9. c=Ceshi("wys")#------setattr-----key:name---value:wys
  10. c.age=12#------setattr-----key:age---value:12
  11. del c.age#delattr-----age
  12. print(c.age)#

当在删除对象的属性的时候,会触发__delattr__函数,这里跟__setattr__函数的触发机制是一样的,在这里要做删除对象的属性操作,注意,不能写del self.item,也不能使用delattr()函数,回形成无限递归。正确姿势应该是self.__dict__.pop(item)。同理,我们也可以在__delattr__中添加一些特殊的控制

  1. class Ceshi:
  2. def __init__(self,n):
  3. self.name=n
  4. def __setattr__(self,key,value):
  5. self.__dict__[key]=value
  6. def __delattr__(self,item):
  7. self.__dict__.pop(item)
  8.  
  9. c=Ceshi("wys")#------setattr-----key:name---value:wys
  10. c.age=12#------setattr-----key:age---value:12
  11. del c.age#delattr-----age
  12. print(c.age)#

getattr当在对象.dict找不到的属性时候,就会触发这个函数

  1. class A:
  2. def __init__(self,name):
  3. self.name=name
  4. def __getattr__(self,item):
  5. print("--getattr---%s"%item)
  6.  
  7. a=A("wys")
  8. a.name#到着一步也没有__getattr__函数
  9. 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]

  1. class Mydict:
  2. def __init__(self,name):
  3. self.name=name
  4. def __getitem__(self,item):
  5. print("--getitem--")
  6. return self.__dict__[item]
  7. def __setitem__(self,key,value):
  8. print("--setitem--")
  9. self.__dict__[key]=value
  10. def __delitem__(self,key):
  11. print("--delitem---")
  12. self.__dict__.pop(key)
  13. def __setattr__(self, item, value):
  14. print("--setattr--")
  15. self.__dict__[item] = value
  16.  
  17. def __delattr__(self, item):
  18. print("--delattr--")
  19. return self.__dict__.pop(item)
  20.  
  21. # def __delattr__(self,item):
  22. # 'del self.item时,触发该方法'
  23.  
  24. f=Mydict("yangzai")#触发了__setattr__方法
  25. f["myclass"]="3b"#触发了__setitem__方法
  26. print(f["myclass"])#触发了__getitem__方法
  27. f["age"]=18#触发__setitem__方法
  28. del f["age"]#触发__delitem__方法
  29.  
  30. print(f.myclass)#触发了__getattr__方法
  31. print(f.name)#触发__getattr__
  32. f.name="wys2"#触发__setattr__方法
  33. print(f.__dict__)#{'age': 18, 'name': 'wys2'}
  34. del f.age#触发__delattr__方法

doc

表示对象的描述信息。不能被继承

  1. class A:
  2. '着是描述信息'
  3. def ceshi(self):
  4. 'this is a ceshi func'
  5. pass
  6. print(A.__doc__)#---------->着是描述信息
  7. a=A()
  8. print(a.ceshi.__doc__)#----->this is a ceshi func

slots

限制实例的属性,是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)。slots属性就规定了,实例化后的对象只能有固定的一些属性,不会再创建命令空间,这样的好处是可以节省内容空间。这样的设计常用于那些属性是固定的,并且要实例化大量的对象的场景

  1. class A:
  2. __slots__=["x","y"]
  3. a=A()
  4. a.x=10
  5. a.y=12
  6. a.z=232#报错,AttributeError: 'A' object has no attribute 'z'
  7. print(a.y)#---->12
  8. print(a.x)#---->10
  9. print(a.__dict__)#报错,AttributeError: 'A' object has no attribute '__dict__'
  10. 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完就完成了。

  1. class Foo:
  2. def __init__(self,st,end,buc=1):#模拟range的功能
  3. self.start=st
  4. self.end=end
  5. self.buc=buc
  6. def __iter__(self):
  7. return self#迭代器__iter__方法得到自己本身
  8. def __next__(self):
  9. if self.start>self.end-1:#模拟超出next范围值之后就抛出StopIteration
  10. raise StopIteration
  11. n=self.start
  12. self.start+=self.buc
  13. return n
  14.  
  15. f=Foo(1,20,5)
  16.  
  17. # print(next(f))
  18. # print(next(f))
  19. # print(next(f))
  20.  
  21. for i in f:#for可自动捕捉异常
  22. print(i)
  23.  
  24. from collections import Iterable,Iterator
  25. print(isinstance(f,Iterator))
  26. print(isinstance(f,Iterable))

析构函数del

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的

  1. class Foo:
  2. def __init__(self,file,mode="r",encode="utf8"):
  3. self.f=open(file,mode=mode,encoding=encode)
  4. def __getattr__(self,item):
  5. print("---getattr--")
  6. return getattr(self.f,item)
  7.  
  8. def __del__(self):
  9. print("__del__")
  10. self.close()#会触发__getattr__方法
  11.  
  12. f=Foo("a.txt")
  13. del f #解除f跟值的绑定关系,触发析构函数
  14.  
  15. --------以下为输出---------
  16. ------------------>
  17. __del__#即使不del f,在文件执行完成后,值的引用计数为0,执行__del__函数
  18. ---getattr--

另外一种情况:

  1. f=Foo("a.txt")
  2. f2=f
  3. del f#并不会触发__del__函数,因为还有f2保持对值Foo("a.txt")的引用
  4. print("-------------->")

或者是:

  1. f=Foo("a.txt")
  2. #在执行完上述操作后,仍然会执行__del__,因为python的解释器会定期回收垃圾

上下文管理协议 enter 和exit

  1. with open("a.txt",encoding="utf8",mode="r")

上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enter和exit方法

  1. import time
  2. class Open:
  3. def __init__(self,filpath,mode="w+",encode="utf8"):
  4. self.path=filpath
  5. self.x=open(filpath,mode=mode,encoding=encode)#文件句柄
  6. def write(self,value):
  7. t=time.strftime("%Y-%m-%d")
  8. self.x.write(t+value)#操作文件句柄,然后自定义要写入的内容
  9. def __getattr__(self,item):#当找不到属性readseekflush等属性的时候,触发
  10. #self.x.read 错误
  11. if hasattr(self.x,item):
  12. return getattr(self.x,item)
  13. def __enter__(self):
  14. print("__enter__")
  15. return self
  16. def __exit__(self, exc_type, exc_val, exc_tb):
  17. 'exc_type:异常类型。exc_val:异常信息。exc_tb:异常的追踪信息'
  18. print("__exit__")
  19. self.close()#在这里关闭文件
  20. return True #代表已经处理结果了(程序就不会抛异常)
  21. def __del__(self):
  22. print("--del--")
  23. self.close()
  24. with Open("a.txt"):
  25. print("可以不用as f,语法上没有问题")
  26.  
  27. with Open("a.txt") as f:#实际上是把Open("a.txt").__enter__()赋值给f
  28. f.write("")
  29. raise TypeError("error")
  30. -------以下为输出---------
  31. __enter__#先执行__enter__方法,拿到实例化后的对象,并赋值给f
  32. 23232#再执行with代码体,执行终止后(如果发生异常也终止),就去执行__exit__,如果是异常终止,会将异常传给__exit__方法
  33. __exit__#最后执行__exit__方法

call

对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

  1. class Peopel:
  2. pass
  3.  
  4. peo=Peopel()#只所有可以加()运行,是因为元类type里有__call__方法
  5. peo()#报错,对象peo会首先去找__call__方法,Peopel类中没有__call__绑定方法
  1. class Peopel:
  2. def __call__(self):
  3. print("call")
  4.  
  5. print(callable(Peopel))#-------->True
  6. peo=Peopel()
  7. print(callable(peo))#--------->True
  8. peo()#完美运行

元类

创建类三点要素:类名,类的命名空间,类的继承。可通过type()方式手动创建一个类。type是元类,是类的类

创建一个类:type(类名:"ceshi",类的继承:(),类的命令空间:{})。

  1. ce=type("ceshi",(),{})
  2. print(ce)#-----》<class '__main__.ceshi'>
  3. print(ce.__bases__)#默认会有----->(<class 'object'>,)
  4. print(ce.__dict__)#会有一些默认饿属性---->{'__doc__': None, '__dict__': <attribute '__dict__' of 'ceshi' objects>, '__weakref__': <attribute '__weakref__' of 'ceshi' objects>, '__module__': '__main__'}
  5.  
  6. class ceshi:
  7. pass
  8.  
  9. #上边两种创建方式是一样的
  10.  
  11. #手动创建一个=自定义属性的类
  12. x=1
  13. def run(self):
  14. print("run")
  15. class_name="myclass"
  16. class_bases=(object,)
  17. class_dict={
  18. "x":1,
  19. "run":run
  20. }
  21. mc=type(class_name,class_bases,class_dict)#创建了一个对象--》类
  22. print(type(mc))#---------><class 'type'>
  23. 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,在自定义的类中,去设置元类为自定义的元类。然后就可以通过自定义的元类去控制类的产生

  1. class Mymeta(type):
  2. pass
  3.  
  4. #Mymeta("Foo",(object,),{"x":1,"run":run})
  5. class Foo(metaclass=Mymeta):#默认情况下,metaclass=type
  6. x=1
  7. def __init__(self):
  8. pass
  9. def run(self):
  10. 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可以去控制类

  1. class Mymeta(type):#自定义元类,继承元类type
  2. def __init__(self,class_name,class_bases,class_dic):
  3. for key in class_dic:#检测如果类里函数没有写doc注释,就抛异常
  4. if not callable(class_dic[key]):continue
  5. if not class_dic[key].__doc__:
  6. raise TypeError("the func or bound method must be hava doc")
  7.  
  8. #type.__init__(self,class_name,class_bases,class_dic)#到元类里去初始化,这里如果不写这句代码,也会默认去调用元类type的__init__方法
  9.  
  10. def __call__(self,*args,**kwargs):#类Foo要加括号()运行,就是因为元类中有这个方法。流程如下:
  11. 'self:Foo'
  12. obj=self.__new__(self)#用Foo下面的一个__new__方法产生一个空对象,obj为空
  13. self.__init__(obj,*args,**kwargs)#然后Foo再掉__init__方法,将空对象和参数传入,进行初始化操作
  14. return obj#一定要返回这个初始化后的对象否则返回的为None
  15.  
  16. #Mymeta("Foo",(object,),{"x":1,"run":run})
  17. class Foo(metaclass=Mymeta):#默认情况下,metaclass=type
  18. x=1
  19. def __init__(self,name):
  20. 'init obj'
  21. self.name=name#obj.name=name
  22. pass
  23. def run(self):
  24. 'run'
  25. print("running")
  26.  
  27. f=Foo("wys")
  28. print(f.name)

元类是深度的魔法,一般在研究大型框架或者超大型项目时才用得的到,而且对元类的使用也会非常的明确。

python基础之魔法方法的更多相关文章

  1. python类之魔法方法

    python类之魔法方法: class A(object): def __init__(self,x): self.x = x def __neg__(self): print('-v') def _ ...

  2. python里的魔法方法1(构造与析构)

    魔法方法——构造与析构 1.python编程的魔法方法: (1)魔法方法总是被双下划线包围,例如__init__: (2)魔法方法是面向对象的python的一切. 2.__new__(class[,… ...

  3. Python学习8——魔法方法、特性和迭代器

    Python中很多名称比较古怪,开头和结尾都是两个下划线.这样的拼写表示名称有特殊意义,因此绝不要在程序中创建这样的名称.这样的名称中大部分都是魔法(方法)的名称.如果你的对象实现了这些方法,他们将在 ...

  4. Python中的魔法方法

    1.什么是魔法方法? 魔法方法就是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而这一 ...

  5. Python 入门之Python基础数据类型及其方法

    Python 入门之Python基础数据类型 1. 整型:int 用于计算,用于比较 (在赋值的时候先执行等号右边的内容) 1.1 整数的加 a = 10 b = 20 print(a + b) 结果 ...

  6. python基础函数、方法

    python的函数和方法,通过def 定义: 函数的特性: 减少重复代码 使程序变的可扩展 使程序变得易维护 函数和方法的区别:函数有返回值.方法没有 语法定义: def sayhi():#函数名 p ...

  7. python中类的魔法方法

    __xx__这种方法,在Python中均称为魔法方法 1.__init__(self) 该方法的作用是初始化对象 在创建对象时被默认调用,不需要手动调节 self参数不需要开发者传递,解释器会自动将创 ...

  8. python的常用魔法方法详细总结

    构造和初始化 __init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数". 实际上, 当我们调用x = SomeClass()的时候调用,__init_ ...

  9. Python基础之公共方法

    公共方法:就是列表,元组,字典,字符串能共同使用的方法: Python内置函数 内置函数罗列 函数 描述 备注 len(item) 计算容器中的元素个数 del(item) 删除变量 del有两种方法 ...

随机推荐

  1. 前台时间格式 2019-03-09T16:00:00.000Z

    问题描述: 本想在前台把字符串格式的日期(2019-03-09)转换成日期格式(2019-03-09 00:00:00),但当把这个参数传到后台去后却变成了2019-03-08T16:00:00.00 ...

  2. AirtestIDE实践二:Poco框架试用

    上一篇用airtest框架做了一个梦幻西游手游的DEMO,这次看看poco的强大之处.首先安装poco:pip install pocoui 其次,把SDK集成到你家游戏中,我这直接用官网提供的一个U ...

  3. 初学Direct X(9) ——文字的显示

    初学Direct X(9) --文字的显示 本次学习如何使用ID3DXFont创建字体,使得我们可以在任何安装了Windows系统中TrueType字体来打印文字,不过最好使用标准字体,这样文字在每一 ...

  4. leetcode-回文链表

    请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true 进阶:你能否用 O(n) 时间复杂 ...

  5. 【转】: 探索Lua5.2内部实现:虚拟机指令(2) MOVE & LOAD

    name args desc OP_MOVE A B R(A) := R(B) OP_MOVE用来将寄存器B中的值拷贝到寄存器A中.由于Lua是register based vm,大部分的指令都是直接 ...

  6. 【Linux 运维】linux系统修改主机名

    主机名的修改:  1.命名解释: [root@localhost~]# 分别代表: 用户名(root) 主机名(localhost) 当前路径(~,当前用户的home目录) 权限标志位(#代表root ...

  7. python leveldb 文档

    标签(空格分隔): python leveldb import leveldb db = leveldb.LevelDB('./db') db.Put('hello', 'world') print ...

  8. WebApi中利用Razor模板引擎来生成html

    在服务器端基于Razor来生成html的一个思路 using System.Web.Mvc; using System.IO; using System.Web.Routing; using Syst ...

  9. java超强分页标签演示

    最近在做一个项目,用到了一个分页,于是动手写了个分页标签,先将代码贴出来,供大家交流,写的不好,请见谅!. 以下是java标签类,继承自SimpleTagSupport package com.lyn ...

  10. jQuery实现仿京东商城图片放大镜

    效果图: 不废话直接上代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...