item函数,slots,迭代器协议,上下文管理,元类

一. item函数

item函数可以把对象操作模拟成字典操作, item函数包括以下三个函数__getitem____setitem____delitem__
具体用法如下所示

class Foo:
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):
        return self.__dict__[item]

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __delitem__(self, key):
        self.__dict__.pop(key)

f = Foo('j')
print(f['name'])
f['gender'] = 'male'
print(f.__dict__)

del f['gender']
print(f.__dict__)

>>j
>>{'name': 'j', 'gender': 'male'}
>>{'name': 'j'}

二. slots

  1. __slots__是什么:是一个类变量,变量值可以是列表,元组,或者其他可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
  2. 当一个类有较少的属性,但是会多次实例化时,为了节省内存可以使用__slots__取代实例的__dict__
    当你定义__slots__后。类的实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
    字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__的一个缺点就是我们不能给实例添加新的属性,只能使用在__slots__中定义的那些属性名。
  3. 注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,应该只在那些经常被使用到 的用作数据结构的类上定义__slots__
    __slots__可以作为一个封装工具来防止用户给实例增加新的属性。使用__slots__可以达到这样的目的,但是__slots__的设计初衷是一个内存优化工具。
class Foo:
    __slots__='x'

f1=Foo()
f1.x=1
f1.y=2#报错
print(f1.__slots__) #使用__slots__后,不再有__dict__

class Bar:
    __slots__=['x','y']
    
n=Bar()
n.x,n.y=1,2
n.z=3#报错

三. 迭代器协议

迭代器协议: 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个stopIteration的异常终止迭代
遵循迭代器协议使用__iter____next__函数实现range函数

class Range:
    def __init__(self, value, start=0, step=1):
        self.value = value
        self.start = start
        self.step = step

    def __iter__(self):    # 使用__iter__函数为Range类增加可迭代属性
        return self

    def __next__(self):   # 使用__next__方法得到迭代返回值
        if self.start >= self.value:
            raise StopIteration
        n = self.start
        self.start += self.step
        return n

for i in Range(3):
    print(i)

四. 上下文管理

操作文件的时候可以使用with语句,with语句遵循上下文协议。使用__enter____exit__方法可以让一个对象支持上下文协议。
使用with语句可以实现with中的语句执行结束后自动完成清理工作
一般在文件操作,网络连接和锁的编程环境中,在__exit__中设计自动释放资源的机制,实现自动释放资源

class Foo:
    def __init__(self, filepath, mod='r', encode='utf-8'):
        self.f = open(filepath, mod, encoding=encode)
        self.filepath = filepath
        self.mod = mod

    def write(self, line):
        self.f.write(line)

    def __getattr__(self, item):
        return getattr(self.f, item)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
        del self.f
        return True   # 返回True可以吞掉异常,异常发生后,`with`代码块外的语句还可以继续执行

    def __del__(self):
        print('close file')

with Foo('a.txt', 'w') as f:
    f.write('ddddd')
    print('------>')

五. 元类

__call__

构造方法的执行是由创建对象触发的,即:对象 = 类名() ;
对于 __call__方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:
    def __init__(self):
        print('__init__')
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')


obj = Foo()  # 执行 __init__
obj()  # 执行 __call__

>>__init__
>>__call__

metaclass

元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
创建类的两种方法

# 方法一
class Foo_1:
    x = 1
    def func(self):
        print('func')
print(Foo_1.__dict__)

#{'__module__': '__main__', 
# 'x': 1, 
# 'func': , 
# '__dict__': , 
# '__weakref__': , 
# '__doc__': None}
#{'__module__': '__main__',

# 方法二
def func(self):
    print('func')
x = 1
Foo_2 = type('Foo_2', (object, ), {'func': func, 'x': 1})
print(Foo_2.__dict__)

#{'__module__': '__main__',
# 'x': 1,
# 'func': , 
# '__dict__': , 
# '__weakref__': , 
# '__doc__': None}

一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

class Mymeta(type):
    def __init__(self,name,bases,dic):
        print('Mymeta.__init__')

    def __new__(cls, *args, **kwargs):
        print('Mymeta.__new__')
        return type.__new__(cls,*args,**kwargs)

    def __call__(self, *args, **kwargs):
        print('Mymeta.__call__')
        obj=self.__new__(self)
        self.__init__(self,*args,**kwargs)
        return obj

class Foo(object,metaclass=Mymeta):
    def __init__(self,name):
        print('Foo.__init__')
        self.name=name
    def __new__(cls, *args, **kwargs):
        print('Foo.__new__')
        return object.__new__(cls)

f = Foo('Joe')

>>Mymeta.__new__
>>Mymeta.__init__
>>Mymeta.__call__
>>Foo.__new__
>>Foo.__init__

名字加括号的本质(即,任何name()的形式),都是先找到name的父类,然后执行:父类.__call__

父类.__call__一般做两件事:
调用父类.__new__方法并返回一个对象
调用父类.__init__方法对子类进行初始化

class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行
Foo=Mymeta('foo',(...),{...})
因此我们可以看到,只定义class就会有如下执行效果

Mymeta.__new__
Mymeta.__init__

实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作,
遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的父类type,然后执行type.__call__(...)方法
于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化

f=Foo('Joe')的原理同上

你可能感兴趣的:(item函数,slots,迭代器协议,上下文管理,元类)