面向对象进阶(二)

一 __setitem__,__getitem,__delitem__

调用方法:实例化名[属性名]

__setattr__,__delattr__,__getattr__用法相同,只是调用方法不同:实例化名.属性名

#把对象操作属性模拟成字典的格式
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('egon')
print(f.name) # 同print(f['name']) 调用__getitem__方法

f['name']='egonlinhai'#调用__setitem__方法
del f['name'] #调用__delitem__方法

二 __slots__

1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,而且属性相同,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
class Foo:
    __slots__='x'


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

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

三 __next__和__iter__实现迭代器协议

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

    def __iter__(self):
        return self

    def __next__(self):        
        self.x+=1
        return self.x

f=Foo(3)
for i in f:
    print(i)

三 __doc__

class Foo:
    '我是描述信息'
    pass

class Bar(Foo):
    pass
print(Bar.__doc__) #该属性无法继承给子类

四 __module__和__class__

  __module__ 表示当前操作的对象在那个模块

  __class__ 表示当前操作的对象的类是什么

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C:

    def __init__(self):
        self.name = ‘SB'
lib.aa
from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类

五 __del__

  析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1    #删除后,f1的引用计数为0了,触发__del__函数
print('------->')

#输出结果
执行我啦
------->
class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
print('------->')

#输出结果
------->
执行我啦   #程序结束也会触发__del__函数

六 __enter__和__exit__

 我们知道在操作文件对象的时候可以这么写

 with open('a.txt') as f:
   '代码块'

上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)  #异常类型
        print(exc_val)  #异常的值
        print(exc_tb)  #异常的
        return True



with Open('a.txt') as f:   #相当于拿到一个对象,可以不写as f  #res = Foo().__enter__() #obj=res
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
  print(“----------”) #不执行,一抛出异常就执行__exit__程序,退出whith语句的子代码块

print(“----------”)  #如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
#执行顺序先执行对象类中的__enter__,然后执行with语句的子代码块,一抛异常,执行对象类中的__exit__

  

用途或者说好处:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

七 __call__

对象后面加括号,触发执行。

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

 

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


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

八 metaclass

1 引子

 class Foo:
     pass
 
 f1=Foo() #f1是通过Foo类实例化的对象

python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)

上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?

 class Foo:
     pass
 
 f1=Foo() #f1是通过Foo类实例化的对象
#type函数可以查看类型,也可以用来查看对象的类,二者是一样的
 print(type(f1)) # 输出:<class '__main__.Foo'>     表示,obj 对象由Foo类创建
 print(type(Foo)) # 输出:<type 'type'>  

2 什么是元类?

元类是类的类,是类的模板

元类是用来控制如何创建类的,正如类是创建对象的模板一样

元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例Foo类是 type 类的一个实例)

type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

3 创建类的两种方式

方式一:

 class Foo:
     def func(self):
         print('from func'

方式二:

#type成为元类,是所有类的类,利用type模拟class关键字的创建类的过程
def run(self):
print('%s is runing' %self.name)

class_name='Bar' #定义类名
bases=(object,)  #定义继承的父类
class_dic={
'x':1,
'run':run
}

Bar=type(class_name,bases,class_dic) #(类名,继承的父类名,class_dic)
print(Bar)
print(type(Bar))

4 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)

自定制元类,定制函数必须有注释信息

class Mymeta(type):  #伪装成元类
     def __init__(self,class_name,class_bases,class_dic):
         # print(self)
         # print(class_name)
         # print(class_bases)
         # print(class_dic)
         for key in class_dic:
            if not callable(class_dic[key]):continue
            if not class_dic[key].__doc__:
                raise TypeError('小子,你没写注释,赶紧去写')
       
class Foo(metaclass=Mymeta):  
#这一步就是在实现Foo=Mymeta('Foo',(object,),{'x':1,'run':run})
    x=1
    def run(self):
        'run function'
        print('running')

 

class Mymeta(type):
     def __init__(self,class_name,class_bases,class_dic):
            pass
     def __call__(self, *args, **kwargs):
        obj=self.__new__(self)   #产生空对象,一个属性也没有
        self.__init__(obj,*args,**kwargs) #调用Foo的__init__,#obj.name='egon'
        return obj
class Foo(metaclass=Mymeta):
    x=1
    def __init__(self,name):
        self.name=name #obj.name='egon'
    def run(self):
        print('running')

f=Foo('egon')
print(f)
print(f.name)
元类控制创建类的行为

 

 

 

 

posted @ 2017-04-25 18:28  shy车队破风手  阅读(167)  评论(0)    收藏  举报