部分魔法方法的概念总结
语句 | 魔法方法 | Python 概念 |
---|---|---|
obj[key] obj[i:j] obj[i:j:k] | __getitem__(key) | 可订阅对象 |
with obj:… | __enter__/__exit__ | 上下文管理器 |
for i in obj:… | __iter__/__next__ __len__/__getitem__ | 可迭代对象 序列 |
obj.<attribute> | __getattr__ | 动态属性检索 |
obj(*args,**kwargs) | __call__(*args,**kwargs) | 可调用对象 |
示例
1. __getitem__ 可订阅对象
my_numbers = (4,5,3,9)
print (my_numbers[-1]) # 9
print (my_numbers[-3]) # 5
my_numbers = (1,1,2,3,5,8,13,21)
print (my_numbers[2:5]) # (2, 3, 5) 使用切片获取多个元素 从第一个数字索引(包含)开始,直到第二个数字索引(不包含)的所有元素
print(my_numbers[:3]) #(1, 1, 2) 将获取 数字索引3(不包含)之前的所有元素
print(my_numbers[3:]) #(3, 5, 8, 13, 21) 将获取 数字索引3(包含)之后的所有元素
print(my_numbers[::]) #(1, 1, 2, 3, 5, 8, 13, 21) 获取 整个元组
print(my_numbers[1:7:2]) #(1, 3, 8) 此处 第三个参数代表步长 用于表示在区间内迭代时每次要跳过多少个元素
print(my_numbers) #(1, 1, 2, 3, 5, 8, 13, 21)
以上功能能够实现,要归功于__getitem__ 此魔法方法;当类似myobject[key]之类的方法被调用时会掉用这个魔法方法,它将键值(方括号内的值)作为参数传递。
class Items:
def __init__(self,*values):
self._values = list(values)
def __len__(self):
return len(self._values)
def __getitem__(self, item):
return self._values.__getitem__(item)
my_number_items = Items(1,2,3,4,5,6)
print(my_number_items[0]) # 会执行 \_\_getitem__ 方法
print(my_number_items[:3]) # 会执行 \_\_getitem__ 方法
2. __enter__/__exit__ 上下文管理器
使用:
'''with 语句将调用 __enter__ 方法, 无论这个方法返回什么,都将分配给as 后边的变量;
当执行完process_file(fd) 方法后 会调用__exit__ 方法, 即使上下文管理器中发生异常或错误
__exit__方法任然会被调用'''
with open(filename) as fd:
process_file(fd)
示例:
def stop_database():
print ("stop")
def start_database():
print ("start")
class DBHandler:
def __enter__(self):
stop_database()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
start_database()
def db_backup():
print("pg_dump database")
def main():
with DBHandler():
db_backup()
main()
#stop
#pg_dump database
#start
import contextlib
@contextlib.contextmanager
def db_handler():
stop_database()
yield
start_database()
with db_handler():
db_backup
# 注:contextlib.ContextDecorator Python3.6 及之后版本可用
class dbhandler_decorator(contextlib.ContextDecorator):
def __enter__(self):
stop_database()
def __exit__(self, ext_type, ex_value, ex_traceback):
start_database()
@dbhandler_decorator()
def offline_backup():
print("pg_dump database")
offline_backup()
#stop
#pg_dump database
#start
3. __iter__/__next__ 创建可迭代对象 (注: Python3.6 及之后版本可用)
from datetime import timedelta, date
class DataRangeIterable():
""" An iterable that contains its own iterator object"""
def __init__(self, start_date, end_date):
self.start_date = start_date
self.end_date = end_date
self._present_day = start_date
def __iter__(self):
# current_day = self.start_date
# while current_day < self.end_date:
# yield current_day
# current_day += timedelta(days=1)
return self
def __next__(self):
if self._present_day > self.end_date:
raise StopIteration
today = self._present_day
self._present_day += timedelta(days=1)
return today
for day in DataRangeIterable(date(2018,1,1),date(2018,1,5)):
print(day)
# 2018-01-01
# 2018-01-02
# 2018-01-03
# 2018-01-04
4. __len__/__getitem__ 创建序列
class DataRangeIterable():
""" An iterable that contains its own iterator object"""
def __init__(self, start_date, end_date):
self.start_date = start_date
self.end_date = end_date
# self._present_day = start_date
self._range = self._create_range()
def _create_range(self):
days = []
current_day = self.start_date
while current_day < self.end_date:
days.append(current_day)
current_day += timedelta(days=1)
return days
def __getitem__(self,day_no):
return self._range[day_no]
def __len__(self):
return len(self._range)
for day in DataRangeIterable(date(2018,1,1),date(2018,1,5)):
print(day)
# 2018-01-01
# 2018-01-02
# 2018-01-03
# 2018-01-04
5. __getattr__ 动态属性检索
class DynamicAttributes:
def __init__(self, attribute):
self.attribute = attribute
def __getattr__(self,attr):
if attr.startswith("fallback_"):
name = attr.replace("fallback_", "")
return f"[fallback resolved] {name}"
raise AttributeError(f"{self.__class__.__name__} has no attribute {attr}")
dyn = DynamicAttributes("value")
print(dyn.attribute) # value
print(dyn.fallback_test) # [fallback resolved] test
print(dyn.fallback) # 引发 raise时会抛出此提示 AttributeError: DynamicAttributes has no attribute fallback
dyn.__dict__["fallback_new"] = "new value"
print(dyn.fallback_new) # new value
print(getattr(dyn,"something", "default")) # default
6. __call__(*args,**kwargs) 可调用对象
我们尝试像执行常规函数一样执行对象时,会调用名为__call__的魔法方法。传递给对象的每个参数都将单独传递给__call__方法。
from collections import defaultdict
class CallCount:
def __init__(self):
self._counts = defaultdict(int)
def __call__(self, argument):
self._counts[argument] += 1
return self._counts[argument]
cc = CallCount()
print(cc(1)) # 1
print(cc(2)) # 1
print(cc(1)) # 2
print(cc(1)) # 3
print(cc("something"))# 1