python 魔法方法概述及示例

部分魔法方法的概念总结

语句 魔法方法 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

猜你喜欢

转载自blog.csdn.net/qq_42855293/article/details/118582689