由于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基础之魔法方法的更多相关文章

  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. Qt-QPalette-调色板学习

    已经很久没有更新博客了,一是因为换了公司,完全是断网开发了,没有时间来写博客,最主要的就是温水煮青蛙,自己在舒适的环境中越来越懒了,最近打算强制自己更新一波.不知道能坚持多久.由于目前没有具体的Qt项 ...

  2. jmeter常用测试元件

    1.线程组 线程组是任何测试计划的起点,所有的逻辑控制器和采样器都必须放在线程组下.其他的测试元件(例如监听器)可以直接放在测试计划下,这些测试元件对所有的线程组都生效. 每一个JMeter线程都会完 ...

  3. uiautomatorviewer定位App元素

    这个工具是Android SDK自带的, 日常的工作中经常要使用的, 在C:\Android\sdk\tools\bin目录下: 双击之, 请注意, 我一般选择第一个机器人小图标Device Scre ...

  4. Selenium 入门到精通系列:三

    Selenium 入门到精通系列 PS:Driver_Element 常用方法 例子 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2 ...

  5. JavaScriptSerializer的实现-常用JsonHelper类

    最近开始自己写自己的项目了,终于鼓起勇气迈出了自己认为的这一大步! 先来通用的helper类和大家分享一下 ,第一个是Object转为json序列的类,这个网上有很多,但我实践了一下大部分都不能用的, ...

  6. Python入门(3)

    一.列表 列表是用来储存和处理多个数据的数据类型,我们可以像下面这样来创建一个列表: my_list = [1, 2, 3] 列表和数学中的集合很像,但是,列表中的数据是可以重复,并且他们是有序的,列 ...

  7. priority_queue(优先队列):排序不去重

    C++优先队列类似队列,但是在这个数据结构中的元素按照一定的断言排列有序. 头文件:#include<queue> 参数:priority_queue<Type, Container ...

  8. 【转载】android 常用开源框架

    对于Android初学者以及对于我们菜鸟,这些大神们开发的轻量级框架非常有用(更别说开源的了). 下面转载这10个框架的介绍:(按顺序来吧没有什么排名). 一.  Afinal 官方介绍: Afina ...

  9. BluetoothServerSocket详解

    一. BluetoorhServerSocket简介 1. 继承关系 public final class BluetoothServerSocket extends Object implement ...

  10. <Effective C++>读书摘要--Inheritance and Object-Oriented Design<一>

    1.Furthermore, I explain what the different features in C++ really mean — what you are really expres ...