面向对象最后进阶

1.__slots__

1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.定义__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#报错

2.__next__和__iter__实现迭代器协议

迭代器协议模拟range(start,end)

# 迭代器协议模拟range(start,end)
class Foo:
    def __init__(self,start,end):
        self.start = start
        self.end = end

    def __iter__(self):
        return self


    def __next__(self):
        if self.start >= self.end:
            raise StopIteration
        n = self.start
        self.start += 1
        return n

f = Foo(0,10)
print(list(f))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

迭代器协议实现斐波那契数列

# 斐波那契数列类
class Fib:
    def __init__(self):
        self.__a = 0
        self.__b = 1

    def __iter__(self):
        return self


    def __next__(self):
        if self.__a > 100:
            raise StopIteration
        self.__a,self.__b = self.__b,self.__a+self.__b
        return self.__a

f = Fib()
l = [i for i in f]
print(l)      # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

3.__module__和__class__

__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么

4.__del__析构方法

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

# __del__析构函数
import time
class Foo:
    def __init__(self, path, mode="r", encode="utf8"):
        self.f = open(path, mode=mode, encoding=encode)

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


    def __del__(self):
        print("------del------")
        self.f.close()

f = Foo("a.txt", "w")
f.write("qqqqqqqqqqqqqqqqqqqqqqq\n")

del f          # 清除对象时会触发执行,------del------
# f.close()
print("********************")
time.sleep(5)

5.__enter__和__exit__

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

2.__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

3.如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

# 上下文管理协议实现打开文件
class Foo:
    def __init__(self,path, m="r", e = "utf-8"):
        self.f = open(path, mode=m, encoding=e)


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


    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type, exc_val, exc_tb)
        return 1


with Foo("a.txt", "w") as f:  # 相当于拿到Foo.__enter__()的返回值赋值给f
    print("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-")
    raise NameError("name cuowu")
    print("-"*50)

print("主代码还在继续执行")

# 执行结果
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
# <class 'NameError'> name cuowu <traceback object >
# 主代码还在继续执行

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

6.__call__

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

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

#\_\_call__
class Foo:
    def __call__(self, *args, **kwargs):
        print("__call__ is running")
    pass

f = Foo() 
print(callable(Foo)) # True. 执行 __init__,类()是执行类下的__init__方法
print(callable(f))   # True

7.元类

元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。—— Tim Peters
创建类的另一种方式

# 利用type产生元类
SB = type("SB", (object,), {})
s = SB()
s.name = "zou"
print(type(s))     # <class '__main_ _.SB'>
print(s.__dict__)  # {'name': 'zou'}

元类总结

#元类总结
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('aaa')
        obj=self.__new__(self)
        self.__init__(self,*args,**kwargs)
        return obj

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

'''
需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__

而爹.__call__一般做两件事:
1.调用name.__new__方法并返回一个对象
2.进而调用name.__init__方法对儿子name进行初始化
'''

'''
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__方法对对象进行初始化
'''

'''
obj=Foo('egon')
的原理同上
'''

'''
总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了
1.谁后面跟括号,就从谁的爹中找__call__方法执行
type->Mymeta->Foo->obj
Mymeta()触发type.__call__
Foo()触发Mymeta.__call__
obj()触发Foo.__call__
2.__call__内按先后顺序依次调用儿子的__new__和__init__方法
'''

限制注释信息

# 3 自定义元类限制注释信息
from collections import Iterable,Iterator
class Mymeta(type):
    def __init__(self, class_name, base=None, dict=None ):
        # print(self)
        # print(class_name)
        # print(base)
        # print(dict)
        for key in dict:
            if not callable(dict[key]):continue
            if not dict[key].__doc__:
                raise Exception("你还没写注释信息")

    def __call__(self, *args, **kwargs):
        print("from mytype",self,args,kwargs) # from mytype <class '__main__.Foo'> ('zou',) {}
        obj = self.__new__(self)
        self.__init__(obj, *args, **kwargs)
        return obj


class Foo(metaclass=Mymeta):
    x = 1
    def __init__(self,name):
        "初始化信息"
        self.name = name
    def run(self):
        "run function"
        print("running")


f = Foo("zou")      # from mytype <class '__main__.Foo'> ('zou',) {}
# print(Foo.__dict__)
posted @ 2017-04-25 19:52  pirate邹霉  阅读(116)  评论(0编辑  收藏  举报