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': <function Foo_1.func at 0x000002018A47EAE8>, 
# '__dict__': <attribute '__dict__' of 'Foo_1' objects>, 
# '__weakref__': <attribute '__weakref__' of 'Foo_1' objects>, 
# '__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': <function func at 0x0000020189FB3E18>, 
# '__dict__': <attribute '__dict__' of 'Foo_2' objects>, 
# '__weakref__': <attribute '__weakref__' of 'Foo_2' objects>, 
# '__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')的原理同上

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容