| 
 | 
 
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册  
 
x
 
 本帖最后由 Stubborn 于 2019-10-28 18:31 编辑  
 
 
 
前言:说起来这个魔法函数,刚刚学到对象的魔法函数,大多数都是懵懵懂懂,我就是,反正学完之后,我对魔法函数的应用还只停留在__init__初始化上面,到前些天,我在学习Python核心编程上,才有新的理解,可能看完Python基础课程的[扩展阅读]Python 魔法方法详解我们都会知道,魔法函数可以这么用,但是具体它的应用场景在哪里,应该怎么用?心里面肯定是一团浆糊,以至于到现在,我基本都是用__init__,没有用过其他的魔法函数,好了前言废话说到这里,开始正文。 
 
 
其实相对魔法函数详细介绍,我更愿意用Python自带的abc模块下的collections模块来介绍,我想会比较好理解,魔法函数可以怎么用,应该这样用。在Python的lib目录下面,可以看到一个‘_collections_abc.py’文件,这里抽出顶部一些做讲解: 
 
- __all__ = ["Awaitable", "Coroutine",
 
 -            "AsyncIterable", "AsyncIterator", "AsyncGenerator",
 
 -            "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
 
 -            "Sized", "Container", "Callable", "Collection",
 
 -            "Set", "MutableSet",
 
 -            "Mapping", "MutableMapping",
 
 -            "MappingView", "KeysView", "ItemsView", "ValuesView",
 
 -            "Sequence", "MutableSequence",
 
 -            "ByteString",
 
 -            ]
 
  复制代码 
 
其中__all__包括了所有的抽象基类,我更愿意称呼他们协议,由于这里是讲解魔法函数,这里就不过多讲解,这里就抽取Sequence例子,其他更多的源码,有兴趣的朋友,可以自己去看源码。 
 
[b]点击进Sequence的源码:Ps.不喜欢看源码,可以跳过,直接看下面实例讲解[/b] 
- class Sequence(Reversible, Collection):
 
  
-     """All the operations on a read-only sequence.
 
  
-     Concrete subclasses must override __new__ or __init__,
 
 -     __getitem__, and __len__.
 
 -     """
 
  
-     __slots__ = ()
 
  
-     @abstractmethod
 
 -     def __getitem__(self, index):
 
 -         raise IndexError
 
  
-     def __iter__(self):
 
 -         i = 0
 
 -         try:
 
 -             while True:
 
 -                 v = self[i]
 
 -                 yield v
 
 -                 i += 1
 
 -         except IndexError:
 
 -             return
 
  
-     def __contains__(self, value):
 
 -         for v in self:
 
 -             if v is value or v == value:
 
 -                 return True
 
 -         return False
 
  
-     def __reversed__(self):
 
 -         for i in reversed(range(len(self))):
 
 -             yield self[i]
 
  
-     def index(self, value, start=0, stop=None):
 
 -         '''S.index(value, [start, [stop]]) -> integer -- return first index of value.
 
 -            Raises ValueError if the value is not present.
 
  
-            Supporting start and stop arguments is optional, but
 
 -            recommended.
 
 -         '''
 
 -         if start is not None and start < 0:
 
 -             start = max(len(self) + start, 0)
 
 -         if stop is not None and stop < 0:
 
 -             stop += len(self)
 
  
-         i = start
 
 -         while stop is None or i < stop:
 
 -             try:
 
 -                 v = self[i]
 
 -                 if v is value or v == value:
 
 -                     return i
 
 -             except IndexError:
 
 -                 break
 
 -             i += 1
 
 -         raise ValueError
 
  
-     def count(self, value):
 
 -         'S.count(value) -> integer -- return number of occurrences of value'
 
 -         return sum(1 for v in self if v is value or v == value)
 
  
- Sequence.register(tuple)
 
 - Sequence.register(str)
 
 - Sequence.register(range)
 
 - Sequence.register(memoryview)
 
  复制代码 
 
其父类Reversible源码: 
- class Reversible(Iterable):
 
  
-     __slots__ = ()
 
  
-     @abstractmethod
 
 -     def __reversed__(self):
 
 -         while False:
 
 -             yield None
 
  
-     @classmethod
 
 -     def __subclasshook__(cls, C):
 
 -         if cls is Reversible:
 
 -             return _check_methods(C, "__reversed__", "__iter__")
 
  复制代码 
 
其Reversible父类Iterable源码: 
- class Iterable(metaclass=ABCMeta):
 
  
-     __slots__ = ()
 
  
-     @abstractmethod
 
 -     def __iter__(self):
 
 -         while False:
 
 -             yield None
 
  
-     @classmethod
 
 -     def __subclasshook__(cls, C):
 
 -         if cls is Iterable:
 
 -             return _check_methods(C, "__iter__")
 
 -         return NotImplemented
 
  复制代码 
 
其父类Collection源码: 
- class Collection(Sized, Iterable, Container):
 
  
-     __slots__ = ()
 
  
-     @classmethod
 
 -     def __subclasshook__(cls, C):
 
 -         if cls is Collection:
 
 -             return _check_methods(C,  "__len__", "__iter__", "__contains__")
 
 -         return NotImplemented
 
  复制代码 
 
其Collection父类Sized源码: 
- class Sized(metaclass=ABCMeta):
 
  
-     __slots__ = ()
 
  
-     @abstractmethod
 
 -     def __len__(self):
 
 -         return 0
 
  
-     @classmethod
 
 -     def __subclasshook__(cls, C):
 
 -         if cls is Sized:
 
 -             return _check_methods(C, "__len__")
 
 -         return NotImplemented
 
  复制代码 
 
其Collection父类Container源码: 
- class Container(metaclass=ABCMeta):
 
  
-     __slots__ = ()
 
  
-     @abstractmethod
 
 -     def __contains__(self, x):
 
 -         return False
 
  
-     @classmethod
 
 -     def __subclasshook__(cls, C):
 
 -         if cls is Container:
 
 -             return _check_methods(C, "__contains__")
 
 -         return NotImplemented
 
  复制代码 
 
应用场景:假如我们需要一个类,可支持切片,索引,求长度,元素判断in,以及支持元素反转的“容器”类,知道一个怎么设计吗?如上面源码:其实Sequence就是一个序列类。如果我们自己实现一个,需要实现什么代码,才可以把这个类叫做序列类呢?(Ps:鸭子类型:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子,在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。)我们应该实现什么方法,才能让这只“鸟”(即将实现的类)看起来会像一只“鸭子”(序列类) 
 
现在反转到上面,看下Sequence的源码,我们设计一个序列类的鸭子,需要实现什么方法才能让这只鸟看起来比较想鸭子! 
 
从Sequence源码看出我们需要实现的魔法方法__getitem__ 
从父类Reversible知道我们需要实现的魔法方法__iter__ 
从父类Container知道我们需要实现的魔法方法__contains__ 
 
从Reversible的父类Iterable知道我们需要实现的魔法方法__reversed__ 
从Container的父类Sized知道我们需要实现的魔法方法__len__ 
 
当我们的鸟类实现了以上的魔法方法,它将会看起来像一只鸭子类(序列类) 
 
因为上面提到的魔法函数,因为使用@abstractmethod装饰函数将会限制,要求其子类,派生类,在继承是,必须进行重构,否则会报错 
 
- class Group(object):
 
 -     """支持切片操作,实现这个协议,需要实现一下魔法函数"""
 
 -     def __init__(self, name, company, staffs):
 
 -         self.name = name
 
 -         self.company = company
 
 -         self.staffs = staffs
 
  
-     def __reversed__(self):
 
 -         self.staffs.reverse()
 
  
-     def __getitem__(self, item):
 
 -         """
 
 -         注意,如果是切片,item则是一个slice,如果是取值,item是一个int
 
 -         在对实例化对象进行索引,切片在这里实现对应的逻辑
 
 -         """
 
 -         import numbers
 
 -         cls = type(self)
 
 -         if isinstance(item, slice):
 
 -             return cls(name=self.name, company=self.company, staffs=self.staffs[item])
 
 -         elif isinstance(item, numbers.Integral):
 
 -             return cls(name=self.name, company=self.company, staffs=[self.staffs[item]])
 
  
-     def __len__(self):
 
 -         """在对实例对象求长度"""
 
 -         return len(self.staffs)
 
  
-     def __iter__(self):
 
 -         """返回一个迭代器,支持被遍历"""
 
 -         return iter(self.staffs)
 
  
-     def __contains__(self, item):
 
 -         """实例对象支持in操作,"""
 
 -         if item in self.staffs:
 
 -             return True
 
 -         return False
 
  复制代码 
 
如上,我们对我们的【鸟】类,实现了一些方法,让它跑起来,叫起来,像一只【鸭子】,那么这个【鸟】类,就可以被叫做【鸭子类】【Ps:序列类】 
实现如上的魔法函数后,我们可以对实例对象,进行切片,索引,迭代,in。但是这个实例不支持一些运行操作,因为我们未实现关于运算的魔法方法。有兴趣的伙伴,可以自己进行实现哦 
 
上面我们实现了一个序列类,我更喜欢叫它序列协议,下面再抽两个出来,讲解下有关于上下文管理协议,以及迭代器协议 
 
 
上下文管理协议(with 语句) 
前言: 
        我们学到文件管理的时候,应该都会接触到这一句,with open(path,'w') as f: 知道他可以用来写文件。可能你一般不知道,为什么这个可以写文件,为什么要用这个写文件,怎么设计一个类似的函数,或者类,可以实现这样的功能,这里就涉及到我们的上下文管理协议了。 
 
正文: 
        其实如果你接触之后,就会知道,其实,with语句从根本上来说它就是try excepe finally的简化语句,一个协议都会有对应的魔法函数去支持这个协议,而对上下文管理协议的魔法函数则是这两个__enter__,__exit__,当我们实现这两个魔法函数,可以使我们的【鸟】变的像一只【鸭子】 
- class Open(object):
 
 -     """上下文管理器协议,实现enter,exit魔法函数,就可以使用with语句"""
 
  
-     def write(self):
 
 -         print("this is write")
 
  
-     def __enter__(self):
 
 -         print('__enter__\t 获取资源')
 
 -         return self
 
  
-     def __exit__(self, exc_type, exc_val, exc_tb):
 
 -         print('__exit__\t释放资源')
 
 -         print(exc_type)
 
 -         print(exc_val)
 
 -         print(exc_tb)
 
 -         print("-----end-----")
 
 -         """返回True表示吞掉异常,False抛出异常"""
 
 -         return True
 
 
  复制代码 
看,我们让鸟变成了鸭子(上下文管理协议),现在这个鸭子可以支持with Open() as op: 操作了。Ps.实际上,python为我们提供更简单的方法,函数装饰器,即可实现上下文管理器协议 
- # 使用contextlib简化上下文管理器
 
 - import contextlib
 
  
- @contextlib.contextmanager
 
 - def file_open(file, method):
 
 -     print(f"This is file:{file}")
 
 -     # __enter__
 
 -     yield {"file_open":"fp"}
 
 -     print(f"this is method:{method}")
 
 -     # __exit__
 
  
 
- with file_open("test.txt", "a") as fp:
 
 -     print(f"this is fp:{fp}")
 
  复制代码 
 
 
迭代协议 
前言: 
        说起迭代,可能很多人都会知道,例如,列表->lsit,字符串->srt,元祖->tuple,他们都是可以迭代的对象,有想过,他们为什么可以进行迭代的操作?因为它们实现了Iterable(可迭代)和Iterator(迭代器),实现了迭代协议,进而支持迭代。迭代器是什么?迭代器是访问集合内元素的一种方式,一般用来遍历数据,迭代器和以下标的访问器不一样,迭代器是不能返回的,迭代器提供了一种惰性方式访问数据 
 
正文: 
        听起来很迷糊,真正接触后,你会觉得,其他它们并不困难,现在我们需要实现一个迭代协议,在此之前,用例子简介下,Iterable(可迭代)和Iterator(迭代器)的不同 
- from collections.abc import Iterable,Iterator
 
 - a = [1,2]
 
 - print(isinstance(a, Iterator))
 
 - print(isinstance(a, Iterable))
 
  
- iter_rator = iter(a)
 
 - print(isinstance(iter_rator, Iterator))
 
  复制代码 
 
 
自己实现的迭代器类 
- from collections.abc import Iterable,Iterator
 
 - class Company:
 
  
-     def __init__(self, var):
 
 -         self.var = var
 
  
-     def __iter__(self):
 
 -         """优先考虑"""
 
 -         return Iterators(self.var)
 
  
 
- class Iterators(Iterator):
 
 -     """迭代器"""
 
 -     def __init__(self, var):
 
 -         self.var = var
 
 -         self._index = 0
 
  
-     def __next__(self):
 
 -         # 真正返回迭代值的逻辑
 
 -         try:
 
 -             result = self.var[self._index]
 
 -         except IndexError:
 
 -             raise StopIteration
 
 -         self._index += 1
 
 -         return result
 
  
- if __name__ == '__main__':
 
 -     company = Company([1,2,3,4,5])
 
 -     # for 循环做的事
 
 -     my_itor = iter(company)
 
 -     while True:
 
 -         try:
 
 -             print(next(my_itor))
 
 -         except StopIteration:
 
 -             break
 
  
-     for item in company:
 
 -         print(item)
 
  复制代码 
 
结尾:相信经过几个例子的介绍,大家对魔法函数,不会那么陌生了。有什么疑问在评论区留言。 |   
 
 
 
 |