文章目录
一.定义
在python中,所有用“__”包起来的方法,都统称为“magic method”,中文称为【魔术方法】,例如类的初始化方法 _ _ init_ _ 就是一个魔术方法。
二.特殊属性
python中有几个比较容易理解的魔术方法,是类里面固有的几个特殊属性,总结如下:
魔术方法名称 | 功能 |
---|---|
name | 类、函数、方法的名字 |
module | 类定义所在的模块,若不是当前模块,则返回真实的模块 |
class | 对象或类所属的类 |
bases | 当前类的基类(父类) |
doc | 类、函数的文档帮助,没有定义返回None |
mro | 方法解析顺序,即当前对象继承类的顺序 |
dict | 类或实例的属性,可写的字典 |
编写一个程序,实现这些魔术方法:
from collections import Counter
class A(object):
pass
class C(object):
pass
class Date(A,C):
"""
时间类的封装
"""
country='China'
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
print(Date.__name__)
#类定义所在的模块,如果在当前脚本,返回__main__,否则,返回真实的模块名eg:collections
print(Date.__module__)
print(Counter.__module__)
#对象或类所属的类,是由哪个类实例化出来的
d=Date(2011,10,2)
print(d.__class__)
print(Date.__class__)
#当前类的基类
print(Date.__bases__)
print(A.__bases__)
print(object.__bases__)
#类、函数的文档帮助,没有定义为None
print(Date.__doc__)
#方法解析顺序,继承顺序,实质上是显示类的继承顺序,python3中多继承算法是广度优先
print(Date.__mro__)
#类或实例的属性,可写的字典
print(Date.__dict__ )
结果
Date
__main__
collections
<class '__main__.Date'>
<class 'type'>
(<class '__main__.A'>, <class '__main__.C'>)
(<class 'object'>,)
()
时间类的封装
(<class '__main__.Date'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>)
{'__module__': '__main__', '__doc__': '\n 时间类的封装\n ', 'country': 'China', '__init__': <function Date.__init__ at 0x000001D87E494D08>}
_ _ dir _ _ 查看属性,功能是返回类或对象的所有成员名称列表。
dir()函数就是调用 _ _ dir _ _ ()
如果dir([object])参数obj包含方法_ _ dir _ _ (),该方法将被调用;如果obj 不包含_ _ dir _ _ (),该方法将最大限度收集属性信息。
dir(object) 对于不同类型的对象obj具有不同的行为:
1.如果对象是模块对象,返回的列表包含模块的属性名和变量名
2.如果对象是类型或类对象,返回的列表包含类的属性名以及它基类的属性名
3.如果obj不写,即dir(),返回列表包含内容不同:(1)在模块中,返回模块的属性和变量名 (2)在函数中,返回本地作用域的变量名 (3)在方法中,返回本地作用域的变量名
程序实现它的使用方法:
class A(object):
pass
class C(object):
pass
class Date(object):
"""
时间类的封装
"""
country='china'
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
print(dir(Date))
print(dir())
def add(x,y):
print(dir())
return x+y
add(1,2)
结果
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'country']
['A', 'C', 'Date', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
['x', 'y']
三.对象的创建、初始化、调用、销毁
Python中对象的创建、初始化、调用、销毁分别使用_ _ new _ _ 、_ _ init _ _ 、_ _ del _ _ 、_ _ call _ _ 这四种魔术方法实现
(1).四种魔术方法的功能
- _ _ new _ _ 的功能是在创建对象前所作的动作,接受的参数是cls类,负责对象的创建。通过new魔术方法,返回一个对象
- _ _ init _ _ 的功能是在创建对象后,初始化对象,将实例与类属性捆绑,接受的参数是self对象,负责对象的初始化。
- _ _ call_ _ 的功能是调用对象时执行,可以模拟函数的行为。
- _ _ del _ _ 为析构方法,对象删除时,自动调用。注意:当程序运行结束后,自动释放变量信息,自动调用析构函数。
- new方法和init方法共同构成构造函数
(2)四种魔术方法的应用案例
1)一段程序,展示三种魔术方法的实现:
class Student(object):
def __new__(cls, name):
print('正在创建对象。。。')
return super(Student,cls).__new__(cls) #返回父类的new方法,查看new方法做了什么
def __init__(self,name):
print('正在初始化对象。。。。')
self.name=name
def __del__(self):
print('正在删除对象。。。。')
#当程序结束时,自动释放变量信息,调用析构函数,删除变量
st1=Student('张三')
print(st1.name)
运行结果
正在创建对象。。。
正在初始化对象。。。。
张三
正在删除对象。。。。
析构函数在定义是不执行,只用程序结束时,自动调用析构函数,释放变量信息
2)call魔术方法实现缓存(Fib数列的缓存)
from functools import lru_cache
class Fib(object):
@lru_cache(maxsize=1000) #maxsize代表缓存的内存占用值,超过这个值之后,旧的结果就会被释放,然后将新的计算结果进行缓存,其值应当设为2的幂
#还有一个参数typed=True/False 等于True时表示把不同类型的参数结果分开保存
def __call__(self, n):
if n in (1,2):
return 1
else:
return self(n-1)+self(n-2)
fib=Fib()
print(fib(10))
运行结果
55
当定义一个call方法后,实例化对象后,调用时使用对象加(),即fib(),这样直接调用类的对象,就是调用__call__方法。也可以写为:fib.call(10)
@lru_cache(maxsize,typed) :可以将函数计算结果保存起来,作为缓存,在重复计算时直接返回计算结果,减少运行时间。maxsize参数代表缓存的内存占用值,缓存超过这个值时,旧的结果会被释放,将新的结果进行缓存,其值应当设为2 的幂;typed的值为布尔值,代表是否把不同参数类型的结果分开保存。
3)call魔术方法实现类装饰器
装饰器模式时经常使用的一种python设计模式,一般用于函数实现,但是当函数逻辑很复杂,使用装饰器会让函数冗长,需要把每个小的功能抽象,再进行组合。
类装饰器可以解决这个问题。
from functools import wraps
import time
def timeit(unit='s'):
def wrapper1(fun): #类装饰器需要传入参数时,在原来的装饰器函数外面再加一层定义函数
@wraps(fun) #语法糖,装饰器wraps为函数fun增加一个wrapper定义的功能
def wrapper(*args,**kwargs):
if unit=='s':
start_time=time.time()
result=fun(*args,**kwargs)
end_time=time.time()
print('%s函数运行的时间为%.f s'%(fun.__name__,end_time-start_time))
else:
print('当前功能不支持')
return wrapper
return wrapper1
#被装饰的函数在call方法定义中执行
class Timeit(object):
def __init__(self,unit='s'):
self.unit=unit
def __call__(self, fun):
@wraps(fun)
def wrapper(*args,**kwargs):
if self.unit=='s':
start_time=time.time()
result=fun(*args,**kwargs)
end_time=time.time()
print('%s当前函数运行时间为%.1f s'%(fun.__name__,end_time-start_time))
else:
print('当前功能不支持')
return wrapper
@Timeit(unit='s')
def add(num1,num2):
time.sleep(0.33)
return num1+num2
add(2,4)
运行结果
add当前函数运行时间为0.3 s
4)call魔术方法实现偏函数
偏函数partial 通过from functools import partial
- partial(func,*args,**kwargs) , *args是元组,**kwargs也是元组
- 通过partial函数将func函数计算的结果(计算的输入值为*args参数)保存在一个变量中,接下来直接使用这个存有预先计算结果的变量作为函数,计算先前计算的结果与新输入的参数的新的结果
class partial(object):
def __new__(cls,func,*args, **kwargs):
if not callable(func):
raise TypeError("the first argument must be callable")
self=super().__new__(cls)
self.func=func
self.args=args
self.kwargs=kwargs
return self
def __call__(self, *args, **kwargs):
return self.func(*self.args,*args,**self.kwargs,**kwargs)
min_num=partial(min,10,11)
print(min_num(2,3))
运行结果
2
callable()函数用于检查一个兑现是否可以调用
5)call魔术方法实现函数式编程
python中函数式编程功能,如map(),filter(),reduce()
import os
def is_odd(x):
return x%2==1
list1=list(map(lambda x:x**2,[1,2,3,4])) #map()函数,根据给定的函数,对指定的序列做映射
list2=list(map(lambda x,y:x+y,[1,2,3,4],[2,3,4,5]))
list3=list(filter(is_odd,[1,2,3,4]))
print(list1,list2,list3)
import os
class FileAcceptor(object):
def __init__(self,accepted_extendions):
"""
eg:['.png','.jpg']
@param accepted_extendions:可接受的文件扩展
"""
self.accrpted_extensions=accepted_extendions
def __call__(self, filename):
"""
eg:hello.png
@param filename:需要判断的文件名
@return:
"""
#base='base',ext='.jpg'
base,ext=os.path.splitext(filename)
return ext in self.accrpted_extensions
class ImageFileAccpetor(FileAcceptor):
def __init__(self):
image_ext=('.jpg','jepng','.png')
super(ImageFileAccpetor, self).__init__(image_ext)
class ExcelFileAcceptor(FileAcceptor):
def __init__(self):
excel_ext=('.xls','.xlsx')
super(ExcelFileAcceptor, self).__init__(excel_ext)
if __name__ == '__main__':
filename=[
'hello.jpg',
'hello.xls',
'hello.txt'
]
image_file=filter(ImageFileAccpetor(),filename)
excel_file=filter(ExcelFileAcceptor(),filename)
print(list(image_file))
print(list(excel_file))
运行结果
[1, 4, 9, 16] [3, 5, 7, 9] [1, 3]
['hello.jpg']
['hello.xls']
四.可视化
- str()和repr()都是python中的内置函数,是直接用来格式化字符串的函数
- 当使用内置函数str(obj)时,自动执行obj._ _ str _ _ ()魔术方法
- 当使用内置函数repr(obj)时,自动执行obj. _ _ repr _ _ ()魔术方法
- 当 _ _ str _ _ 魔术方法不存在时,自动执行_ _ repr_ _()魔术方法的内容
class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age
def __ini__(self):
return int(self.age)
def __repr__(self):
return 'Person<%s>'%(self.name)
p1=Person('fentiao','100')
print(p1)
print(int(p1.age))
运行结果
Person<fentiao>
100
五 .类型转换
魔术方法名称 | 功能 |
---|---|
_ _ int _ _(self) | 转化成整型 |
_ _ long _ _(self) | 转化成长整型 |
_ _ float_ _(self) | 转化成浮点型 |
_ _ complex _ _(self) | 转化成复数型 |
_ _ oct_ _(self) | 转化成八进制 |
_ _ hex_ _ (self) | 转化成十六进制 |
_ _ index_ _(self) | 如果定义了一个可能被用来做切片操作的数值类型,就需要定义index |
_ _ trunc_ _(self) | 当math.trunc(self)使用时被调用_ _ trunc_ _返回自身类型的整型截取 |
_ _ coerce_ (self,other) _ | 执行混合类型的运算 |
六.索引和切片
slice()函数实现切片对象,主要用在切片操作函数里的参数传递
- _ _ setitem _ _:当属性被以索引、切片方式赋值时,会调用该方法
- _ _ getitem _ _:一般如果想使用索引、切片访问元素时,就可以在类中定义该方法
- _ _ delitem _ _:当使用索引、切片删除属性时调用该方法
class Student(object):
def __init__(self,name,scores):
self.name=name
self.scores=scores
def __getitem__(self, index):
"""实现获取索引和切片的魔术方法"""
print(index)
return self.scores[index]
def __setitem__(self, index, value):
"""实现修改/设置索引和切片值得魔术方法"""
self.scores[index]=value
def __delitem__(self, index):
del self.scores[index]
stu1=Student('张三',[100,98,96])
stu2=Student('李四',[76,97,86])
print(stu1[1:])
stu1[1:]=[99,99]
print(stu1.scores)
del stu1[1:]
print(stu1.scores)
for i in stu1.scores:
print(i)
运行结果
slice(1, None, None)
[98, 96]
[100, 99, 99]
[100]
100
七.重复、连接与成员操作符
- _ _ mul _ _():重复操作符,实现*的功能,具体返回什么取决于代码的业务要求
- _ _ add_ _ ():连接操作符,连接的必须是同一种数据类型
- _ _ contains _ _ ():成员操作符,判断某个元素是否存在于这个对象中
class Student(object):
def __init__(self,name,scores):
self.name=name
self.scores=scores
def __mul__(self, other):
"""重复操作符"""
print('重复中')
return self.scores*other
def __add__(self, other):
"""连接操作符"""
print('连接中')
return [item[0]+item[1] for item in zip(self.scores,other.scores)]
def __contains__(self, item):
"""成员操作符"""
return item in self.scores
def __iter__(self):
"""iter可将可迭代对象转化为迭代器(可以调用next方法)"""
return iter(self.scores)
stu1=Student('张三',[100,98,96])
stu2=Student('李四',[76,97,86])
print(stu1*3)
print(stu1+stu2)
print(150 in stu2)
运行结果
重复中
[100, 98, 96, 100, 98, 96, 100, 98, 96]
连接中
[176, 195, 182]
False
八.循环
class Student(object):
def __init__(self,name,scores):
self.name=name
self.scores=scores
def __iter__(self):
"""iter可将可迭代对象转化为迭代器(可以调用next方法)"""
return iter(self.scores)
stu1 = Student('张三', [100, 98, 96])
stu2 = Student('李四', [76, 97, 86])
for item in stu2.scores:
print(item)
运行结果
76
97
86
九.with语句安全上下文
with语句操作的对象必须是上下文管理器。
1.什么是上下文管理器
(1).简单的理解,拥有_ _ enter_ _() 和 _ _ exit _ _()方法的对象就是上下文管理器
- 首先 _ _ enter _ _ (self) :进入上下文管理器自动调用的方法在with执行之前运行。如果有as子句,该方法的返回值被赋值给as子句后的变量;该方法可以返回多个值。
- 然后 _ _ exit _ _(self,exc_type,exc_value,exc_traceback):退出上下文管理器自动调用的方法在with之后执行。
(2).执行顺序 - 当with as操作上下文管理器时,就会在执行语句体前,先执行上下文管理器的 _ _ enter _ _()方法
- 然后在执行语句体后,最后执行 _ _ exit _ _()方法
2.构建上下文管理的方法
(1).基于生成器
装饰器context.contextmanager,来定义自己所需要的基于生成器的上下文管理器
import contextlib
import tempfile
import shutil
@contextlib.contextmanager
def make_temp_dir():
try:
tmp_dir=tempfile.mkdtemp()
yield tmp_dir
finally:
shutil.rmtree(tmp_dir)
with make_temp_dir() as f:
pass
(2)基于类的上下文管理器
只要一个类实现了_ _ enter _ _() 和 _ _ exit _ _()这两个方法,程序就可以使用with as 语句来管理它。
class Connect(object):
def __init__(self,filename):
self.filename=filename
def __enter__(self):
self.f=open(self.filename)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
print('with语句执行之后')
self.f.close()
#connect就是上下文管理器。拥有__enter__()和__exit__()方法的对象就是上下文管理器
with Connect('C:/用户/79291/桌面/1_2019.12.13电子三路看房情况.doc') as conn:
pass
十.魔术方法汇总
比较大小
魔术方法名称 | 功能 |
---|---|
_ _ gt _ _ (self,other) | 判断大小 |
_ _ ge _ _ (self,other) | 判断大于等于 |
_ _ eq _ _ (self,other) | 判断等于 |
基本的魔法方法
有关属性的魔术方法
比较操作符
算数运算符
反运算
增量赋值运算
一元操作符
类型转换
上下文管理(with 语句)
容器类型