什么是魔术方法?
在Python中,所有用"__"包起来的方法,都称为【魔术方法】(eg: len, init)。 魔术方法一般是为了让显示器调用的,你自己并不需要调用它们。
一、特殊属性
查看属性:dir 返回类或者对象的所有成员名称列表。dir() 函数就是调用__dir__()。
1). 如果dir([obj]) 参数obj包含方法 dir(),该方法将被调用。
2). 如果obj 不包含 dir(),该方法将最大限度收集属性信息。
二、创建、初始化与销毁
python 中__new__ , init , __call__的区别?
答:1). new的功能是在生成对象之前执行的内容,接受的参数是cls 类, 负责对象的创建
2). init的功能是在对象生成之后执行的内容, 接受的参数是self 对象, 负责对象的初始化
3). call的功能是在调用对象时执行的内容, 可以模拟函数的行为.
当我们新建一个对象 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()
__new__
__init__
__call__
__del__
应用范例: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)
fib = Fib()
print(fib(100)) # 1 1 2 3 5 8
354224848179261915075
call魔术方法实现类装饰器
from functools import wraps
import time
def timeit(unit='s'):
def wrapper1(fun): # fun=add
@wraps(fun)
def wrapper(*args, **kwargs):
if unit == 's':
start_time = time.time()
result = fun(*args, **kwargs) # add(1, 2) result=3
end_time = time.time()
print("%s函数运行时间为%.2f s" %(fun.__name__, end_time-start_time))
return result
else:
print("当前功能不支持......")
return wrapper
return wrapper1
# 类装饰器: 装饰器需要传递的参数通过__init__传递进入.被装饰函数执行的内容在__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) #add(1, 2) result=3
end_time = time.time()
print("%s函数运行时间为%.2f s" % (fun.__name__, end_time - start_time))
return result
else:
print("当前功能不支持......")
return wrapper
# @timeit(unit='s') # @wrapper1 ==> add = wrapper1(add) ===> add = wrapper
# def add(num1, num2):
# time.sleep(0.333)
# return num1 + num2
"""
@TimeIt(unit='h')
# 1). TimeIt_obj = TimeIt(unit='h')
# 2). @TimeIt_obj
# 3). add=TimeIt_obj(add)
# 4). add = wrapper
"""
@TimeIt(unit='s')
def add(num1, num2):
time.sleep(0.333)
return num1 + num2
# add(1, 2) ==> wrapper(1, 2)
add(1, 2)
add函数运行时间为0.34 s
应用范例:call魔术方法实现偏函数
偏函数:通过有效地“冻结”预先确定的参数,来缓存函数参数。运行 时获得剩余参数后,解冻并传递到函数中。
from functools import partial
max_100 = partial(max,10, 100) # 返回对象
print(max_100(1, 2, 3)) # 100
100
应用范例: call魔术方法实现函数式编程
Python中的函数式编程功能,如map()、filter()、reduce(),可以使用可调用对象。
from wtforms import StringField,SubmitField
import os
# 作为基类/父类
class FileAcceptor(object):
def __init__(self, accepted_extensions):
"""
eg: ['.png', '.jpg']
:param accepted_extensions: 可以接受的扩展名
"""
self.accepted_extensions = accepted_extensions
def __call__(self, filename):
"""
eg: hello.jpg
:param filename: 需要判断的文件名
:return:
"""
# base = 'hello', ext='.jpg'
base, ext = os.path.splitext(filename)
return ext in self.accepted_extensions
# 子类
class ImageFileAcceptor(FileAcceptor):
def __init__(self):
image_ext = ('.jpg', '.jepg', '.png')
super(ImageFileAcceptor, self).__init__(image_ext)
# 子类
class ExcelFileAcceptor(FileAcceptor):
def __init__(self):
image_ext = ('.xls', '.xlsx')
super(ExcelFileAcceptor, self).__init__(image_ext)
if __name__ == '__main__':
filenames = [
'hello.jpg',
'hello.xls',
'hello.txt'
]
"""
1). ImageFileAcceptor() 实例化对象, 执行__new__和__init__魔术方法。
2). imagefileacceptor_obj
3). imagefileacceptor_obj('hello.jpg') True
3). imagefileacceptor_obj('hello.xls') False
3). imagefileacceptor_obj('hello.txt') False
4). ['hello.jpg']
"""
images_file = filter(ImageFileAcceptor(), filenames)
excels_file = filter(ExcelFileAcceptor(), filenames)
print(list(images_file))
print(list(excels_file))
三、可视化
类型判断要使用type或isinstance,不能通过判断print输出是否带引号来判断输出值的类型。
1). str()与repr()都是python中的内置函数,是直接用来格式化字符串的函数。
2). 当使用内置函数str(obj)时, 自动执行obj.str()魔术方法。
3). 当使用内置函数repr(obj)时, 自动执行obj.repr()魔术方法。
4). 当__str__魔术方法不存在时, 自动执行__repr__()魔术方法的内容。
可视化魔术方法
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __int__(self):
return int(self.age)
# def __str__(self):
# return 'Person<%s>' %(self.name)
def __repr__(self):
return 'Person<%s>' %(self.name)
p1 = Person("fentiao", '100')
print(p1)
print(int(p1))
Person<fentiao>
100
五、类型转换
六、索引和切片;重复,连接与成员操作符;循环
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]
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可以将可迭代对象转换成迭代器(可以调用next方法的)
return iter(self.scores)
stu1 = Student("张三", [100, 90, 100])
stu2 = Student("里斯", [100, 80, 100])
#1). 索引和切片的测试
print(stu1[1:]) # 获取索引/切片值
stu1[1:] = (80, 80) # 设置索引/切片对应的value值
print(stu1.scores)
del stu1[1:] # 删除索引/切片值
print(stu1.scores)
# # # 2). 连接、重复和成员操作符
# # print(stu1*3)
# # print(stu1 + stu2)
# # print(150 in stu1)
#
#
# for item in stu1:
# print(item)
索引和切片的测试:
slice(1, None, None)
[90, 100]
[100, 80, 80]
[100]
连接、重复、成员操作符
[100, 90, 100, 100, 90, 100, 100, 90, 100]
[200, 170, 200]
False
循环:
100
90
100
七、with语句安全上下文
with语句操作的对象必须是上下文管理器。那么,到底什么是上下文管理器呢?
1). 简单的理解,拥有 enter() 和 exit() 方法的对象就是上下文管理器。
enter(self):进入上下文管理器自动调用的方法,在 with 执行之前执行。如果 有 as子句,该 方法的返回值被赋值给 as 子句后的变量;该方法可以返回多个值。
exit(self, exc_type, exc_value, exc_traceback):退出上下文管理器自动调用的方法。在 with 执行之后执行(不管有无异常)。
2). 当 with as 操作上下文管理器时,就会在执行语句体之前,先执行上下文管理器的 enter() 方法, 然后再执行语句体,最后执行 exit() 方法。
构建上下文管理器,常见的有 2 种方式:基于类实现和基于生成器实现
方法一: 装饰器 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
方法二: 基于类的上下文管理器
只要一个类实现了 enter() 和 exit() 这 2 个方 法,程序就可以使用 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('/etc/passwd') as conn:
pass
八、魔术方法比较大小
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)
print(i1 < i2)
print(i1 >= i2)
print(i1 == i2)
print(i1 != i2)
False
False
True
True
False