文章目录
魔术方法
在Python中,所有用" __ " 包起来的方法,都称为“魔术方法”(eg: __ len__, __ init__)。
魔术方法一般是为了让显示器调用的,自己并不需要调用它们。
一、特殊属性
魔术方法名称 | 功能 |
---|---|
__ name __ | 类,函数,方法等的名称 |
__ module __ | 类定义所在的模块 |
__ class __ | 对象或者类所属的类 |
__ bases __ | 当前类的父类 |
__ doc __ | 类,函数的帮助文档,没有定义为None |
__ mro __ | 方法解析顺序 |
__ dict __ | 类或实例的属性,类型为字典 |
__ dir __ | 类或者对象的所有成员名称列表 |
dir() 函数就是调用__dir__()。
查看属性 :
1). 如果dir([obj]) 参数obj包含方法 __ dir__(),该方法将被调用。
2). 如果Obj 不包含 __ dir__(),该方法将最大限度收集属性信息
二、创建,初始化与销毁
1、__ new __ , __ init__ , __ call __的区别
1). __ new __ 的功能是在生成对象之前执行的内容,接受的参数是cls 类, 负责对象的创建
2). __ init __ 的功能是在生成对象之后执行的内容, 接受的参数是self 对象, 负责对象的初始化
3).__ call __的功能是在调用对象时执行的内容, 可以模拟函数的行为
2、创建对象的步骤
当我们新建一个对象 x=someclass() 的时候,经历的步骤:
1). __new__先创建类并返回类的实例。
2). 自动调用__init__来初始化函数的值。
3). 汇总: 第一步和第二步共同构成了构造函数。
4). 对象生命周期调用结束时, __ del __ 方法(构析函数)会被调用。
class Person(object):
def __new__(cls):
print("__new__")
return object.__new__(cls) #要返回一个对象
def __init__(self):
print("__init__")
def __call__(self, *args, **kwargs):
print('__call__')
def __del__(self):
# 析构方法: 当对象被删除或者从内存释放时自动执行
print("__del__")
p1 = Person()
p1()
运行结果
3、关于__ call __魔术方法的一些案例
(1)__ call __ 魔术方法实现缓存
"""__call__ 魔术方法实现缓存"""
from functools import lru_cache
class Fib(object):
@lru_cache(maxsize=1000)
def __call__(self, n):
if n in (1,2):
return 1
else:
return self(n - 1) + self(n - 2) # 调用对象: self()
if __name__ == '__main__':
fib = Fib()
print(fib(20))
(2)__ call __魔术方法实现类装饰器
"""
__call__魔术方法实现类装饰器
在需要传参数时,用类装饰器实现更异读,
把要传的参数写在__init__函数里
装饰的内容写在__call__函数里
"""
from functools import wraps
import time
# 之前传统的装饰器
def timeit(unit):
def wrapper1(func):
@wraps(func)
def wrapper(*args, **kwargs):
if unit == 's':
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print("%s函数运行的时间是: %.2f s" %(func.__name__, end_time-start_time))
return result
else:
print('当前功能不支持')
return wrapper
return wrapper1
#call方法实现 类装饰器
#将要传入的参数,定义在__init__函数里,装饰的代码写在__call__函数里
class Timeit(object):
def __init__(self, unit):
self.unit = unit
def __call__(self,func): # 2
@wraps(func)
def wrapper(*args, **kwargs): # 4
if self.unit == 's':
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print("%s函数运行的时间是: %.2f s" % (func.__name__, end_time - start_time))
return result
else:
print('当前功能不支持')
return wrapper
# #用传统装饰器装饰函数add
# @timeit(unit='h') #语法糖: add = timeit(add) 执行timeit() 返回wrapper1 所以 add=wrapper1
# def add(num1,num2):
# time.sleep(0.333)
# return num1 + num2
#用类装饰器装饰函数add
@Timeit(unit = 's') # 1
# 类名后面跟括号:Timeit() ,是在实例化对象,实例化出一个 object ,
# @object 在语法糖里:add = object(add) ,是在调用对象,执行__call__函数里的内容:进行装饰 返回wrapper , 即 add=wrapper
def add(num1,num2):
time.sleep(0.333)
return num1 + num2
if __name__ == '__main__':
add(1,2) # 3 这里 add=wrapper ,实质是执行 wrapper 函数
(3)__ call __ 魔术方法实现偏函数
"""
偏函数: 冻结预先确定的数
"""
from functools import partial
class partial:
def __new__(cls, func, *args, **kwargs):
if not callable(func): # callable() 函数用于检查一个对象是否是可以调用的
raise TypeError("没有__call__魔术方法")
# 实例化对象,获取对象信息
self = super().__new__(cls) # 调用父类的new方法创建对象
self.func = func
self.args = args
self.krgs = kwargs
return self # new函数一定要返回一个对象
def __call__(self, *args, **kwargs):
return self.func(*self.args, *args, **self.kwargs, **kwargs)
max100 = partial(max , 10, 20 , 100) # 实例化对象
max_num = max100(1,2,3,4) # 调用对象
print(max_num) # 100
(4)__ call __魔术方法实现函数式编程
Python中的函数式编程功能,如map()、filter()、reduce(),可以使用可调用对象。下面是调用对象和filter()根据文件名扩展名过滤文件。
"""call 和 filter 实现文件过滤器"""
import os
# 父类
class FileAccceptor(object):
def __init__(self,accepted_extensions):
"""
:param accepted_extensions:接收的扩展名,例如: .jpg .png
"""
self.accepted_extensions = accepted_extensions
def __call__(self,filename):
"""
:param filename:需要判断的文件名
:return:
"""
base, ext = os.path.splitext(filename) # 切割文件名
return ext in self.accepted_extensions # 判断后缀名是否在
#子类
class ImageFileAcceptor(FileAccceptor):
def __init__(self):
image_ext = ('.jpg', '.jepg', '.png')
super(ImageFileAcceptor, self).__init__(image_ext) # 重写父类init方法
# 继承父类的__call__方法
class ExcelFileAcceptor(FileAccceptor):
def __init__(self):
excel_ext = ('.xls', '.xlsx')
super(ExcelFileAcceptor, self).__init__(excel_ext)
# 继承父类的__call__方法
if __name__ == '__main__':
filenames = [
'hello.jpg',
'hello.xls',
'hello.txt'
]
image_acceptor = ImageFileAcceptor()
excel_acceptor = ExcelFileAcceptor()
#使用filter() 进行筛选
image_file = filter(image_acceptor, filenames) # 写对象名就行,不用加括号,他内置的自动会调用对象 ,就像之前写的函数,也是只写了函数名,没有加括号
excel_file = filter(excel_acceptor, filenames)
# 可以简写为:
# image_file = filter(ImageFileAcceptor(),filenames) # 将filenames 一个个传给实例化出的对象 obj('hello.jpg'),调用call
# excel_file = filter(ExcelFileAcceptor(), filenames)
print(list(image_file))
print(list(excel_file))
三、可视化
str() 与repr() 都是python中的内置函数,是直接用来格式化字符串的函数。
当使用内置函数str(obj)时, 自动执行 obj.__ str __ () 魔术方法。
当使用内置函数repr(obj)时, 自动执行 obj.__ repr __ () 魔术方法。
当__str__魔术方法不存在时, 自动执行 obj.__ repr __ () 魔术方法的内容。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
# def __str__(self):
# return 'Person<%s>' %(self.name)
def __repr__(self):
return 'Person<%s>' %(self.name)
p1 = Person("张三", '20')
print(p1)
运行结果:
如果没有加可视化,运行结果为一个类对象,这样的展示太不友好:
所以在类中定义了__ str __ 或者__ repr __ 魔术方法,就可以友好展示出我们想要的格式
四、类型转换
魔术方法 | 描述 |
---|---|
__ int __ | 转换成整形 |
__ long __ | 转换成长整型 |
___ float __ | 转换成浮点型 |
__ comples __ | 转换成复数型 |
__ oct __ | 转换成八进制 |
__ hex __ | 转换成十六进制 |
__ index __ | 如果定义了可能会用来做切片操作的数值型,可以定义它 |
类中定义了这些类型转换的魔术方法时,才可以使用 int()、float() 等进行数值类型转换
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __int__(self): # 定义__int__魔术方法 转换成int型
return int(self.age)
p1 = Person("张三", '20')
print(p1.age, type(p1.age))
p = int(p1.age) # 进行数据类型转换
print(p , type(p))
五、容器操作
1、索引与切片
(1)拓展小知识: slice() 函数实现切片对象,主要用在切片操作函数里的参数传递,我们所使用的li[:]实现切片实际上是slice()函数在操作
(2)实现索引和切片的魔术方法:
__ setitem __:当属性被以索引、切片方式赋值的时候会调用该方法
__ getitem __:一般如果想使用索引、切片访问元素时,就可以在类中定义这个方法
__ delitem __:当使用索引、切片删除属性时调用该方法
2、重复,连接和成员操作符
重复:__ mul __
连接:__ add __
成员操作符:__ contains __
3、循环
转换为迭代器:__iter __
可以使用for循环访问
class Student(object):
def __init__(self, name, scores):
self.name = name
self.scores = scores
def __getitem__(self, index):
"""获取索引和切片"""
return self.scores[index]
def __setitem__(self, index, value): # 需要传入修改的值
"""修改索引和切片的值"""
self.scores[index] = value
def __delitem__(self, index):
"""删除索引和切片的值"""
del self.scores[index]
def __mul__(self, other):
"""重复操作"""
return self.scores * other
def __add__(self, other):
"""连接操作,传入的是对象"""
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可以将可迭代对象转换成迭代器,实现for循环(可以调用next方法)"""
return iter(self.scores)
if __name__ == '__main__':
s1 = Student('张三', [80, 90, 100])
s2 = Student('李四', [30, 30, 30])
# 索引和切片
print(s1[0])
print(s1[1:])
s1[1:] = [30, 30]
print(s1.scores)
del s1[-1]
print(s1.scores)
#重复,连接,成员操作符
print(s1 * 3)
print(s1 + s2)
print(200 in s1)
#循环
for item in s1:
print(item)
六、比较大小
class Int(object):
def __init__(self, number, weight):
self.number = number
self.weight = weight
def __gt__(self, other):
"""判断大于的魔术方法"""
return self.number * self.weight > other.number * other.weight
def __ge__(self, other):
"""判断大于等于的魔术方法"""
return self.number * self.weight >= other.number * other.weight
def __eq__(self, other):
"""判断等于的魔术方法"""
return self.number * self.weight == other.number * other.weight
i1 = Int(20, 3)
i2 = Int(20, 3)
print(i1 > i2) # False
print(i1 < i2) # False
print(i1 >= i2) # True
print(i1 == i2) # True
print(i1 != i2) # False
七、with语句安全上下文
面试题: 我们在进行文件操作时,常常会用到with语句,with语句操作的对象必须是上下文管理器。那么,到底什么是上下文管理器呢?
(1)简单的理解,拥有 __ enter __() 和 __ exit __() 方法的对象就是上下文管理器。
__ enter __(self):进入上下文管理器自动调用的方法,在 with 执行之前执行。如果 有 as子句,该 方法的返回值被赋值给 as 子句后的变量;该方法可以返回多个值。
__ exit __(self, exc_type, exc_value, exc_traceback):退出上下文管理器自动调用的方法。在 with 执行之后执行(不管有无异常)。
(2)当 with as 操作上下文管理器时,就会在执行语句体之前,先执行上下文管理器的 __ enter __() 方法, 然后再执行语句体,最后执行 __ exit __() 方法。
实现上下文管理器
方法一: 魔术方法实现
class Connect(object):
def __init__(self, filename):
self.filename = filename
# 定义__enter__ 魔术方法 进入
def __enter__(self):
self.f = open(self.filename)
return self.f
# 定义__exit__ 魔术方法 关闭
def __exit__(self, exc_type, exc_val, exc_tb):
print('with语句执行之后......')
self.f.close()
# Connect就是上下文管理器。 拥有 __enter__() 和 __exit__() 方法的对象就是上下文管理器
with Connect('/etc/passwd') as conn:
pass
方法二:装饰器实现
"""
一个上下文管理器的装饰器 : @contextlib.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