python面向对象高级

__str__,__repr__

  • __str__定义在类内部,必须返回一个字符串类型
  • __repr__定义在类内部,必须返回一个字符串类型
  • 打印由这个类产生的对象时,会触发执行__str__,若是没有__str__会触发__repr__
class Bar:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # def __str__(self):
    #     print("this str")
    #     return "名字是%s age%s" % (self.name, self.age)

    def __repr__(self):  # 转换字符串,在解释器中执行
        print("thsi repr")
        return "%s" % self.name

test = Bar("alex", 18)
print(test)
#__str__定义在类内部,必须返回一个字符串类型,
#__repr__定义在类内部,必须返回一个字符串类型,
#打印由这个类产生的对象时,会触发执行__str__,若是没有__str__会触发__repr__
__str__ 和 __repr__

isinstance(obj,cls)和issubclass(sub,super)

  • isinstance(obj,cls)检查对象(obj)是不是类(类的对象)
  • issubclass(sub, super)检查字(sub)类是不是父( super) 类的派生类
class Bar:
    pass
class Foo(Bar):
    pass

obj=Foo()

print(isinstance(obj,Foo))
print(Foo.__bases__)
print(issubclass(Foo,Bar))

"""
True
(<class '__main__.Bar'>,)
True
"""
isinstance issubclass

attr系列

  • __getattr__只有在使用点调用属性且属性不存在的时候才会触发
  • __setattr__添加/修改属性会触发它的执行
  • __delattr__删除属性的时候会触发
class Foo:
    def __init__(self,x):
        self.name=x

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')

    def __setattr__(self, key, value): #这的key类型是str
        print('----> from setattr')
        # if not isinstance(value,str):
        #     raise TypeError('must be str')
        #setattr(self,key,value)和 self.key=value  #这就无限递归了,你好好想一想
        self.__dict__[key]=value

    def __delattr__(self, item): #这的item类型是str
        print('----> from delattr')
        # delattr(self,item)和 del self.item #这就无限递归了
        self.__dict__.pop(item)

# __setattr__添加/修改属性会触发它的执行
f1 = Foo('alex') #设置属性 触发__setattr__
print(f1.__dict__)  # 由于你重写了__setattr__,凡是赋值操做都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操做属性字典,不然永远没法赋值
f1.z = 3  # 添加属性 触发__setattr__
print(f1.__dict__)
setattr(f1,'nn',55) #添加属性 触发__setattr__
print(f1.__dict__)
f1.__dict__['a'] = 3  # 能够直接修改属性字典,来完成添加/修改属性的操做,这样的操做不触发__setattr__
print(f1.__dict__)

# __delattr__删除属性的时候会触发
del f1.a             #触发__delattr__
print(f1.__dict__)
delattr(f1,'nn')    #触发__delattr__
print(f1.__dict__)
f1.__dict__.pop('z')    #不触发__delattr__
print(f1.__dict__)

# __getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx
attr系列

__getattr__和__getattribute__

  • 当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程当中抛出异常AttributeError
class Foo:
    def __init__(self,x):
        self.x=x

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__
"""
10
执行的是我
"""

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

    def __getattribute__(self, item):
        print('无论是否存在,我都会执行')

f1=Foo(10)
f1.x
f1.xxxxxx
"""
无论是否存在,我都会执行
无论是否存在,我都会执行
"""

class Foo:
    def __init__(self,x):
        self.x=x
    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print('无论是否存在,我都会执行')
        raise AttributeError('哈哈')

f1=Foo(10)
f1.x
f1.xxxxxx
"""
无论是否存在,我都会执行
执行的是我
无论是否存在,我都会执行
执行的是我
"""
__getattribute__

二次加工标准类型(包装)

一、基于继承的原理定制本身的数据类型

包装:python为你们提供了标准数据类型,以及丰富的内置方法,其实在不少场景下咱们都须要基于标准数据类型来定制咱们本身的数据类型,新增/改写方法,这就用到了咱们刚学的继承/派生知识(其余的标准类型都可以经过下面的方式进行二次加工)javascript

class List(list): #继承list全部的属性,也能够派生出本身新的,好比append和mid
    def append(self, p_object):
        ' 派生本身的append:加上类型检查'
        if not isinstance(p_object,int):
            raise TypeError('must be int')
        super().append(p_object)
 
    @property
    def mid(self):
        '新增本身的属性'
        index=len(self)//2
        return self[index]
 
l=List([1,2,3,4])
print(l)
l.append(5)
print(l)
# l.append('1111111') #报错,必须为int类型
 
print(l.mid)
 
#其他的方法都继承list的
l.insert(0,-123)
print(l)
l.clear()
print(l)
继承实现

二、受权的方式实现定制本身的数据类型

受权:受权是包装的一个特性, 包装一个类型一般是对已存在的类型的一些定制,这种作法能够新建,修改或删除原有产品的功能。其它的则保持原样。受权的过程,便是全部更新的功能都是由新类的某部分来处理,但已存在的功能就受权给对象的默认属性。 实现受权的关键点就是覆盖__getattr__方法java

import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
    def write(self,line):
        t=time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' %(t,line))
 
    def __getattr__(self, item):
        return getattr(self.file,item)
 
f1=FileHandle('b.txt','w+')
f1.write('你好啊')
f1.seek(0)
print(f1.read())
f1.close()
受权实现Open
class List:
    def __init__(self,x):
        self.seq=list(x)

    def append(self,value):
        if not isinstance(value,str):
            raise TypeError('must be str')
        self.seq.append(value)
    @property
    def mid(self):
        index=len(self.seq)//2
        return self.seq[index]
    def __getattr__(self, item):
        return getattr(self.seq,item)

    def __str__(self):
        return str(self.seq)

l=List([1,2,3])

l.append('1')

print(l.mid)
l.insert(0,123123123123123123123123123)
# print(l.seq)
print(l)
受权实现List

item系列

  • 经过字典方式,查看修改,类或者对象的属性
  • __setitem__,__getitem,__delitem__
class Foo:
    def __init__(self,name):
        self.name=name

    # 参数类型是字符串

    def __getitem__(self, item):
        print('getitem',type(item))
        print(self.__dict__[item])

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')

    def __setitem__(self, key, value):
        print('setitem',type(key))
        self.__dict__[key]=value

    def __setattr__(self, key, value):
        print('----> from setattr',type(key))
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('del obj[key]时,我执行',type(key))
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('----> from delattr del obj.key时,我执行',type(item))
        self.__dict__.pop(item)

#触发__setattr__
f1=Foo('sb')
f1.gender='male'
setattr(f1,'level','high')

#触发__setitem__
f1['age']=18
f1['age1']=19

#触发__delattr__
del f1.age1
delattr(f1,'level')

#触发__delitem__
del f1['age']

#触发__getattr__
f1.xxxxxx

#触发__getitem__
f1['name']

#什么都不触发
f1.__dict__['test']='aa'
f1.__dict__['test']
f1.__dict__.pop('test')
print(f1.__dict__)
item系列

__format__

  • 自定制格式化字符串__format__
# 和生成实例化对象的内容相对应
date_dic={
    'ymd':'{0.year}:{0.month}:{0.day}',
    'dmy':'{0.day}/{0.month}/{0.year}',
    'mdy':'{0.month}-{0.day}-{0.year}',
}
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

    def __format__(self, format_spec): # format_spec指定格式化的类型
        if not format_spec or format_spec not in date_dic:
            format_spec='ymd'
        fmt=date_dic[format_spec] # 经过date_dic字典格式化成相应格式的类型
        return fmt.format(self) #把对象传入,格式化的内容

d1=Date(2016,12,29)
print(format(d1))
print('{:mdy}'.format(d1))
"""
2016-12-26
20161226
"""
__format__

__slots__

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

p1 = Poo()
p1.x = 1
# p1.y = 2  # 报错
print(p1.__slots__)  #打印x  p1再也不有__dict__


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


class Foo:
    __slots__=['name','age']

f1=Foo()
f1.name='alex'
f1.age=18
print(f1.__slots__) #['name', 'age']

f2=Foo()
f2.name='egon'
f2.age=19
print(f2.__slots__) #['name', 'age']
print(Foo.__dict__)#f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存
"""
{
'age': <member 'age' of 'Foo' objects>, 
'name': <member 'name' of 'Foo' objects>, 
'__module__': '__main__', '__doc__': None, 
'__slots__': ['name', 'age']}ame', 'age']
}
"""
__slots__

__next__和__iter__实现迭代器协议

class Foo:
    def __init__(self,start,stop):
        self.num=start
        self.stop=stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.num >= self.stop:
            raise StopIteration
        n=self.num
        self.num+=1
        return n

f=Foo(1,5)
from collections import Iterable,Iterator
print(isinstance(f,Iterator))

for i in Foo(1,5):
    print(i)

"""
True
1
2
3
4
"""
__iter__,__next__
class Range:
    def __init__(self,start,end,step):
        self.start=start
        self.end=end
        self.step=step

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

    def __iter__(self):
        return self

for i in Range(1,7,2):
    print(i) #1  3   5
简单模拟range
class Fib:
    def __init__(self):
        self._a=0
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        self._a,self._b=self._b,self._a + self._b
        return self._a

f1=Fib()

print(f1.__next__())
print(next(f1))
print(next(f1))

for i in f1:
    if i > 100:
        break
    print('%s ' %i,end='')

'''
1
1
2
3 5 8 13 21 34 55 89 
'''
斐波那契数列

__doc__

  • 它类的描述信息
  • 该属性没法继承给子类
class Foo:
    #我是描述信息
    pass

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

__module__和__class__

  • __module__ 表示当前操做的对象在那个模块
  • __class__ 表示当前操做的对象的类是什么
#test.py
class C:
    def __init__(self):
        self.name = '测试'


#aa.py
from test import C
class Foo:
    pass

f1 = Foo()
print(f1.__module__) #__main__
print(f1.__class__)  #<class '__main__.Foo'>

c1 = C()
print(c1.__module__) #test  即:输出模块
print(c1.__class__)  #<class 'test.C'>  即:输出类
__module__,__class__

__del__析构方法

  • 状况1:当对象在内存中被释放时,自动触发执行。
  • 状况2:人为主动del f对象时(若是引用计数为零时),自动触发执行。
  • 注:若是产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,若是产生的对象的同时还会向操做系统发起系统调用,即一个对象有用户级与内核级两种资源,好比(打开一个文件,建立一个数据库连接),则必须在清除对象的同时回收系统资源,这就用到了__del__
  • 典型的应用场景:
    1. 数据库连接对象被删除前向操做系统发起关闭数据库连接的系统调用,回收资源
    2. 在del f以前保证f.close()执行
class Foo:
    def __del__(self):
        print('执行我啦')
f1=Foo()
del f1
print('------->')
"""
执行我啦
------->
"""


class Foo:
    def __del__(self):
        print('执行我啦')
f1=Foo()
# del f1
print('------->')
"""
------->
执行我啦
"""
__del__
"""
典型的应用场景:
建立数据库类,用该类实例化出数据库连接对象,对象自己是存放于用户空间内存中,而连接则是由操做系统管理的,存放于内核空间内存中
当程序结束时,python只会回收本身的内存空间,即用户态内存,而操做系统的资源则没有被回收,这就须要咱们定制__del__,
在对象被删除前向操做系统发起关闭数据库连接的系统调用,回收资源
这与文件处理是一个道理:

f=open('a.txt') #作了两件事,在用户空间拿到一个f变量,在操做系统内核空间打开一个文件
del f #只回收用户空间的f,操做系统的文件还处于打开状态
#因此咱们应该在del f以前保证f.close()执行,即使是没有del,程序执行完毕也会自动del清理资源,因而文件操做的正确用法应该是
f=open('a.txt')
#读写...
f.close()
#不少状况下你们都容易忽略f.close,这就用到了with上下文管理
"""
import time
class Open:
    def __init__(self,filepath,mode='r',encode='utf-8'):
        self.f=open(filepath,mode=mode,encoding=encode)

    def write(self):
        pass

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

    def __del__(self):
        print('----->del')
        self.f.close()  ##

f=Open('a.txt','w')
f1=f
del f  #f1=f 引用计数不为零
print('=========>')

"""
=========>
----->del
"""
应用场景

__enter__和__exit__实现上下文管理协议

  • with语句(也叫上下文管理协议),为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
  • __exit__()中的三个参数分别表明异常类型(exc_type),异常值(exc_val)和追溯信息(exc_tb),即with语句中代码块出现异常,则with后的代码都没法执行
  • 若是__exit()返回值为True,那么异常会被清空,就好像啥都没发生同样,with后的语句正常执行
  • 用途或者说好处:
    1. 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工做,无须手动干预
    2. 在须要管理一些资源好比文件,网络链接和锁的编程环境中,能够在__exit__中定制自动释放资源的机制
"""
with open('a.txt') as f:
    '代码块'
上述叫作上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
__exit__的运行完毕就表明了整个with语句的执行完毕
"""

class Open:
    def __init__(self,name):
        self.name=name
    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')


with Open('a.txt') as f:
    print('=====>执行代码块')
    # print(f,f.name) #__enter__方法的返回值赋值给as声明的变量

"""
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
"""


#__exit__()中的三个参数分别表明异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都没法执行
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)

with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***') #遇到raise,with里代码就结束,with代码块里raise后面的代码就不会执行,with代码结束就会执行__exit__方法
    print('11111111')  ####永远不会打印
print('0'*100) #------------------------------->不会执行


#若是__exit()返回值为True,那么异常会被清空,就好像啥都没发生同样,with后的语句正常执行
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:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
    print('11111111')  ####永远不会打印
print('0'*100) #------------------------------->会执行   #__exit__方法返回布尔值为true的值时,with代码块后面的代码会执行
__enter__, __exit__
class Open:
    def __init__(self,filepath,mode,encode='utf-8'):
        self.f=open(filepath,mode=mode,encoding=encode)
        self.filepath=filepath
        self.mode=mode
        self.encoding=encode

    def write(self,line):
        print('write')
        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()
        return True

with Open('a.txt','w') as f: #f=Open('a.txt','w')
    f.write('11\n')
    f.write('22\n')
    print(sssssssssssssss) #抛出异常,交给__exit__处理
    f.write('33\n') # 这不执行

print('aaaaa')#执行
模拟Open

property

  • 一个静态属性property本质就是实现了get,set,delete三种方法
#被property装饰的属性会优先于对象的属性被使用,只有在属性sex定义property后才能定义sex.setter,sex.deleter
class People:
    def __init__(self,name,SEX):
        self.name=name
        # self.__sex=SEX  #这不调用@sex.setter  def sex(self,value):方法  由于设置的是__sex 不是 sex,或者说__sex没有被@property装饰
        self.sex=SEX      #①由于sex被@property装饰,因此self.sex=SEX是去找@sex.setter  def sex(self,value): 方法,而不是给对象赋值
    @property
    def sex(self):
        return self.__sex #p1.__sex
    @sex.setter
    def sex(self,value):
        print('...')
        if not isinstance(value,str):
            raise TypeError('性别必须是字符串类型')
        self.__sex=value         #② male给了value ,  self.__sex='male'
    @sex.deleter                      #del p1.sex的时候调用
    def sex(self):
        del self.__sex #del p1.__sex

p1=People('alex','male')  #会调用 @sex.setter  def sex(self,value): 方法是由于__init__中 self.sex=SEX 是给sex设置值了
p1.sex='female' #会调用@sex.setter  def sex(self,value): 方法
del p1.sex
print(p1.sex) #AttributeError: 'People' object has no attribute '_People__sex'
用法1
class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
"""
get的时候运行我啊
set的时候运行我啊
delete的时候运行我啊
"""
用法2
class Goods:
    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8
    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price
    @price.setter
    def price(self, value):
        self.original_price = value
    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
print(obj.price)  #  80.0
obj.price = 200   # 修改商品原价
print(obj.price)  # 160.0
del obj.price     # 删除商品原价
修改价格
#实现类型检测功能

#第一关:
class People:
    def __init__(self,name):
        self.name=name
    @property
    def name(self):
        return self.name

p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,因此你这写会触发property内置的set,抛出异常


#第二关:修订版
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
print(p1.name)
print(p1.name)
print(p1.name)
print(p1.__dict__)

p1.name='egon'
print(p1.__dict__)

del p1.name
print(p1.__dict__)


#第三关:加上类型检查
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print('set------>')
        if not isinstance(value,str):
            raise TypeError('必须是字符串类型')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.DouNiWan

p1=People('alex') #self.name实际是存放到self.DouNiWan里
p1.name=1
类型检查

__call__

  • 对象后面加括号,触发执行。
  • 构造方法的执行是由建立对象触发的,即:对象 = 类名()
  • 而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__')
obj = Foo()  # 执行 __init__
obj()  # 执行 __call__
__call__
class MyType(type):
    def __call__(cls, *args, **kwargs):
        print(cls)
        obj = cls.__new__(cls, *args, **kwargs)
        print('在这里面..')
        print('==========================')
        obj.__init__(*args, **kwargs)
        return obj

class Foo(metaclass=MyType):
    def __init__(self):
        self.name = 'alex'
        print('123')

f = Foo()   #调用
print(f.name)

"""
<class '__main__.Foo'>
在这里面..
==========================
123
alex
"""
__call__, __new__

__annotations__

def test(x:int,y:int)->int:
    return x+y
print(test.__annotations__) 
#{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
__annotations__

描述符(__get__,__set__,__delete__)

  • 描述符本质:
    1. 就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
    2. __get__()调用一个属性时,触发
    3. __set__()为一个属性赋值是,触发
    4. __delete__()采用del删除属性时,触发
  • 描述符的做用:
    1. 是用来代理另一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
  • 描述符分两种:
    1. 数据描述符:至少实现了__get__()和__set__()
    2. 非数据描述符:没有实现__set__()
  • 注意事项:
    1. 描述符自己应该定义成新式类,被代理的类也应该是新式类
    2. 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
    3. 要严格遵循该优先级,优先级由高到底分别是
      1. 类属性
      2. 数据描述符
      3. 实例属性
      4. 非数据描述符
      5. 找不到的属性触发__getattr__()
  • 描述符总结:
    1. 描述符是能够实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
    2. 描述符是不少高级库和框架的重要工具之一,描述符一般是使用到装饰器或者元类的大型框架中的一个组件
class Foo:
    def __get__(self, instance, owner):
        print('触发get')
    def __set__(self, instance, value):
        print('触发set')
    def __delete__(self, instance):
        print('触发delete')

#包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
f1=Foo()
f1.name='egon'
f1.name
del f1.name
#疑问:什么时候,何地,会触发这三个方法的执行

#描述符分两种
#1:数据描述符:至少实现了__get__()和__set__()
class Foo:
    def __set__(self, instance, value):
        print('set')
    def __get__(self, instance, owner):
        print('get')

#2:非数据描述符:没有实现__set__()
class Foo:
    def __get__(self, instance, owner):
        print('get')
引子:描述符类产生的实例进行属性操做并不会触发三个方法的执行
#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

#描述符Int
class Int:
    def __get__(self, instance, owner):
        print('Int调用')
    def __set__(self, instance, value):
        print('Int设置...')
    def __delete__(self, instance):
        print('Int删除...')

class People:
    name=Str()
    age=Int()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age

#何地?:定义成另一个类的类属性

#什么时候?:且看下列演示

p1=People('alex',18)

#描述符Str的使用
p1.name
p1.name='egon'
del p1.name

#描述符Int的使用
p1.age
p1.age=18
del p1.age

#咱们来瞅瞅到底发生了什么
print(p1.__dict__)
print(People.__dict__)

#补充
print(type(p1) == People) #type(obj)实际上是查看obj是由哪一个类实例化来的
print(type(p1).__dict__ == People.__dict__)
描述符应用之什么时候?何地?
类属性>数据描述符
#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

class People:
    name=Str()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age


p1=People('egon',18)

#若是描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操做,于p1自己无关了,至关于覆盖了实例的属性
p1.name='egonnnnnn'
p1.name
print(p1.__dict__)#实例的属性字典中没有name,由于name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
del p1.name
数据描述符>实例属性
实例属性>非数据描述符 
class Foo:
    def __set__(self, instance, value):
        print('set')
    def __get__(self, instance, owner):
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length


#name是一个数据描述符,由于name=Foo()而Foo实现了get和set方法,于是比实例属性有更高的优先级
#对实例的属性操做,触发的都是描述符的
r1=Room('厕所',1,1)
r1.name
r1.name='厨房'



class Foo:
    def __get__(self, instance, owner):
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length


#name是一个非数据描述符,由于name=Foo()而Foo没有实现set方法,于是比实例属性有更低的优先级
#对实例的属性操做,触发的都是实例本身的
r1=Room('厕所',1,1)
r1.name
r1.name='厨房'
再次验证:实例属性>非数据描述符
非数据描述符>找不到
class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3231.3)

#调用
print(p1.__dict__)
p1.name

#赋值
print(p1.__dict__)
p1.name='egonlin'
print(p1.__dict__)

#删除
print(p1.__dict__)
del p1.name
print(p1.__dict__)
类型限制1
class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

#疑问:若是我用类名去操做属性呢
People.name #报错,错误的根源在于类去操做属性时,会把None传给instance

#修订__get__方法
class Str:
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
print(People.name) #完美,解决
类型限制2
class Str:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type): #若是不是指望的类型,则抛出异常
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str('name',str) #新增类型限制str
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常
类型限制3
类型限制4
类型限制5
def decorate(cls):
    print('类的装饰器开始运行啦------>')
    return cls

@decorate #无参:People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3333.3)
类的装饰器:无参
def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>',kwargs)
        return cls
    return decorate
@typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('egon',18,3333.3)
类的装饰器:有参
本身作一个@property
实现延迟计算功能
一个小的改动,延迟计算的好梦就破碎了  
class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #类来调用,instance为None,owner为类自己,实例来调用,instance为实例,owner为类自己,
        def feedback():
            print('在这里能够加功能啊...')
            return self.func(owner)
        return feedback

class People:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls):
        print('你好啊,帅哥 %s' %cls.name)

People.say_hi()

p1=People()
p1.say_hi()
#疑问,类方法若是有参数呢,好说,好说

class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #类来调用,instance为None,owner为类自己,实例来调用,instance为实例,owner为类自己,
        def feedback(*args,**kwargs):
            print('在这里能够加功能啊...')
            return self.func(owner,*args,**kwargs)
        return feedback

class People:
    name='linhaifeng'
    @ClassMethod # say_hi=ClassMethod(say_hi)
    def say_hi(cls,msg):
        print('你好啊,帅哥 %s %s' %(cls.name,msg))

People.say_hi('你是那偷心的贼')

p1=People()
p1.say_hi('你是那偷心的贼')
本身作一个@classmethod
class StaticMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #类来调用,instance为None,owner为类自己,实例来调用,instance为实例,owner为类自己,
        def feedback(*args,**kwargs):
            print('在这里能够加功能啊...')
            return self.func(*args,**kwargs)
        return feedback

class People:
    @StaticMethod# say_hi=StaticMethod(say_hi)
    def say_hi(x,y,z):
        print('------>',x,y,z)

People.say_hi(1,2,3)

p1=People()
p1.say_hi(4,5,6)
本身作一个@staticmethod  

metaclass

  • 元类是类的类,是类的模板
  • 元类是用来控制如何建立类的,正如类是建立对象的模板同样
  • 元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
  • type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。
#type元类,是全部类的类,利用type模拟class关键字的建立类的过程

class Bar2:
    x=1
    def run(self):
        print('%s is runing' %self.name)
print(Bar2.__bases__)
print(Bar2.__dict__)

#用type模拟Bar2
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)
print(Bar) #<class '__main__.Bar'>
print(type(Bar)) #<class 'type'>
print(Bar.__bases__)
print(Bar.__dict__)


#就是新建了一个空类
class Spam2:
    pass

print(Spam2)
print(Spam2.__bases__)
print(Spam2.__dict__)

#用type模拟Spam2
Spam=type('Spam',(),{})
print(Spam)
print(Spam.__bases__)
print(Spam.__dict__)
test 1
class Poo(metaclass=type):  #就是执行了 type('Poo',(object,),{'x':1,'run':...})
    x=1
    def run(self):
        print('running')

#__init__
class Mymeta(type):
     def __init__(self,class_name,class_bases,class_dic): #这的self就是Foo类
         for key in class_dic:
            if not callable(class_dic[key]):continue
            if not class_dic[key].__doc__:
                raise TypeError('你没写注释,赶忙去写')
         # type.__init__(self,class_name,class_bases,class_dic)

class Foo(metaclass=Mymeta):  #就是执行了 Foo=Mymeta('Foo',(object,),{'x':1,'run':...})
    x=1
    def run(self):
        'run function'
        print('running')

#__call__
class Mymeta(type):
     def __init__(self,class_name,class_bases,class_dic):
            pass
     def __call__(self, *args, **kwargs):# print(self) 这的self就是Foo类
        obj=self.__new__(self) #建一个空对象
        self.__init__(obj,*args,**kwargs) #注意参数obj   obj.name='tom'
        return obj                        #把对象返回
class Foo(metaclass=Mymeta):
    x=1
    def __init__(self,name):
        self.name=name #obj.name='tom'
    def run(self):
        print('running')

f=Foo('tom')
print(f)
print(f.name)
test 2
"""
exec:3个参数
参数 1:包含一系列python代码的字符串
参数 2:全局做用域(字典形式),若是不指定,默认为globals()
参数 3:局部做用域(字典形式),若是不指定,默认为locals()
能够把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
"""
g={
    'x':1,
    'y':2
}
l={}

exec('''
global x,z
x=100
z=200

m=300
''',g,l)

print(g) #{'x': 100, 'y': 2,'z':200,......}
print(l) #{'m': 300}
exec的用法
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

obj=Foo()
#一、要想让obj这个对象变成一个可调用的对象,须要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发
#二、调用obj的返回值就是__call__方法的返回值
res=obj(1,2,3,x=1,y=2)
"""
<__main__.Foo object at 0x0000000000B0BEB8>
(1, 2, 3)
{'x': 1, 'y': 2}
"""
__call__
class OldboyTeacher(object):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(type(t1)) #查看对象t1的类是<class '__main__.OldboyTeacher'>
print(type(OldboyTeacher)) # 结果为<class 'type'>,证实是调用了type这个元类而产生的OldboyTeacher,即默认的元类为type

"""
一:
python中一切皆为对象。
全部的对象都是实例化或者说调用类而获得的(调用类的过程称为类的实例化),好比对象t1是调用类OldboyTeacher获得的
元类-->实例化-->类OldboyTeacher-->实例化-->对象t1


二:
class关键字建立类的流程分析:
用class关键字定义的类自己也是一个对象,负责产生该对象的类称之为元类(元类能够简称为类的类),内置的元类为type
class关键字在帮咱们建立类时,必然帮咱们调用了元类OldboyTeacher=type(...),那调用type时传入的参数是什么呢?
必然是类的关键组成部分,一个类有三大组成部分,分别是
一、类名class_name='OldboyTeacher'
二、基类们class_bases=(object,)
三、类的名称空间class_dic,类的名称空间是执行类体代码而获得的
调用type时会依次传入以上三个参数
综上,class关键字帮咱们建立一个类应该细分为如下四个过程:
一、拿到类名:class_name='OldboyTeacher'
二、拿到类的基类们:class_bases=(object,)
三、执行类体代码,拿到类的名称空间:class_dic={...}
四、调用元类获得类:OldboyTeacher=type(class_name,class_bases,class_dic)


三:
自定义元类控制类OldboyTeacher的建立:
一个类没有声明本身的元类,默认他的元类就是type,除了使用内置元类type,
咱们也能够经过继承type来自定义元类,而后使用metaclass关键字参数为一个类指定元类
"""
class Mymeta(type): #只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    pass

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

"""
四:
自定义元类能够控制类的产生过程,类的产生过程其实就是元类的调用过程,
即OldboyTeacher=Mymeta('OldboyTeacher',(object),{...}),
调用Mymeta会先产生一个空对象OldoyTeacher,
而后连同调用Mymeta括号内的参数一同传给Mymeta下的__init__方法,完成初始化,因而咱们能够
"""

class Mymeta(type): #只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    def __init__(self,class_name,class_bases,class_dic):
        # print(self) #<class '__main__.OldboyTeacher'>
        # print(class_bases) #(<class 'object'>,)
        # print(class_dic) #{'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x102b95ae8>, 'say': <function OldboyTeacher.say at 0x10621c6a8>}
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类的功能

        if class_name.islower():
            raise TypeError('类名%s请修改成驼峰体' %class_name)

        if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
            raise TypeError('类中必须有文档注释,而且文档注释不能为空')

class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    """
    类OldboyTeacher的文档注释
    """
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


"""
五:
自定义元类控制类OldboyTeacher的调用:
储备知识:__call__
调用一个对象,就是触发对象所在类中的__call__方法的执行,若是把OldboyTeacher也当作一个对象,
那么在OldboyTeacher这个对象的类中也必然存在一个__call__方法
"""

class Mymeta(type): #只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    def __call__(self, *args, **kwargs):
        print(self) #<class '__main__.OldboyTeacher'>
        print(args) #('egon', 18)
        print(kwargs) #{}
        return 123

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)



# 调用OldboyTeacher就是在调用OldboyTeacher类中的__call__方法
# 而后将OldboyTeacher传给self,溢出的位置参数传给*,溢出的关键字参数传给**
# 调用OldboyTeacher的返回值就是调用__call__的返回值
t1=OldboyTeacher('egon',18)
print(t1) #123


"""
六:
默认地,调用t1=OldboyTeacher('egon',18)会作三件事
一、产生一个空对象obj
二、调用__init__方法初始化对象obj
三、返回初始化好的obj
对应着,OldboyTeacher类中的__call__方法也应该作这三件事
"""
class Mymeta(type): #只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        #一、调用__new__产生一个空对象obj
        obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,表明建立一个OldboyTeacher的对象obj

        #二、调用__init__初始化空对象obj
        self.__init__(obj,*args,**kwargs)

        #三、返回初始化好的对象obj
        return obj

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(t1.__dict__) #{'name': 'egon', 'age': 18}

"""
七:
上例的__call__至关于一个模板,咱们能够在该基础上改写__call__的逻辑从而控制调用OldboyTeacher的过程,
好比将OldboyTeacher的对象的全部属性都变成私有的
"""
class Mymeta(type): #只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        #一、调用__new__产生一个空对象obj
        obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,表明建立一个OldboyTeacher的对象obj

        #二、调用__init__初始化空对象obj
        self.__init__(obj,*args,**kwargs)

        # 在初始化以后,obj.__dict__里就有值了
        obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}
        #三、返回初始化好的对象obj
        return obj

class OldboyTeacher(object,metaclass=Mymeta):
    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

t1=OldboyTeacher('egon',18)
print(t1.__dict__) #{'_OldboyTeacher__name': 'egon', '_OldboyTeacher__age': 18}

"""
八:
再看属性查找:
上例中涉及到查找属性的问题,好比self.__new__
结合python继承的实现原理+元类从新看属性的查找应该是什么样子呢???

在学习完元类后,其实咱们用class自定义的类也全都是对象(包括object类自己也是元类type的 一个实例,能够用type(object)查看),
咱们学习过继承的实现原理,若是把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object
"""
class Mymeta(type): #只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        obj=self.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj

class Bar(object):
    n=333

class Foo(Bar):
    n=222

class OldboyTeacher(Foo,metaclass=Mymeta):
    n=111

    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


print(OldboyTeacher.n) #自下而上依次注释各个类中的n=xxx,而后从新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type

"""
九:
因而属性查找应该分红两层,一层是对象层(基于c3算法的MRO)的查找,另一个层则是类层(即元类层)的查找
#查找顺序:
#一、先对象层:OldoyTeacher->Foo->Bar->object
#二、而后元类层:Mymeta->type
分析下元类Mymeta中__call__里的self.__new__的查找
"""
class Mymeta(type):
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        obj=self.__new__(self)
        print(self.__new__ is object.__new__) #True


class Bar(object):
    n=333

    # def __new__(cls, *args, **kwargs):
    #     print('Bar.__new__')

class Foo(Bar):
    n=222

    # def __new__(cls, *args, **kwargs):
    #     print('Foo.__new__')

class OldboyTeacher(Foo,metaclass=Mymeta):
    n=111

    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


    # def __new__(cls, *args, **kwargs):
    #     print('OldboyTeacher.__new__')


OldboyTeacher('egon',18) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找

"""
十:
总结,Mymeta下的__call__里的self.__new__在OldboyTeacher、Foo、Bar里都没有找到__new__的状况下,
会去找object里的__new__,而object下默认就有一个__new__,因此即使是以前的类均未实现__new__,
也必定会在object中找到一个,根本不会、也根本不必再去找元类Mymeta->type中查找__new__

咱们在元类的__call__中也能够用object.__new__(self)去造对象
但咱们仍是推荐在__call__中使用self.__new__(self)去创造空对象,
由于这种方式会检索三个类OldboyTeacher->Foo->Bar,而object.__new__则是直接跨过了他们三个
"""
class Mymeta(type): #只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    n=444

    def __new__(cls, *args, **kwargs):
        obj=type.__new__(cls,*args,**kwargs) # 必须按照这种传值方式
        print(obj.__dict__)
        # return obj # 只有在返回值是type的对象时,才会触发下面的__init__
        return 123

    def __init__(self,class_name,class_bases,class_dic):
        print('run。。。')


class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object),{...})
    n=111

    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)

print(type(Mymeta)) #<class 'type'>
"""
产生类OldboyTeacher的过程就是在调用Mymeta,而Mymeta也是type类的一个对象,那么Mymeta之因此能够调用,必定是在元类type中有一个__call__方法
该方法中一样须要作至少三件事:
class type:
    def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
        obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象
        self.__init__(obj,*args,**kwargs)
        return obj
"""
分析
在元类中控制把自定义类的数据属性都变成大写
"""
练习二:在元类中控制自定义的类无需__init__方法
  1.元类帮其完成建立对象,以及初始化操做;
  2.要求实例化时传参必须为关键字形式,不然抛出异常TypeError: must use keyword argument
  3.key做为用户自定义类产生对象的属性,且全部属性变成大写
"""
class Mymetaclass(type):
    # def __new__(cls,name,bases,attrs):
    #     update_attrs={}
    #     for k,v in attrs.items():
    #         if not callable(v) and not k.startswith('__'):
    #             update_attrs[k.upper()]=v
    #         else:
    #             update_attrs[k]=v
    #     return type.__new__(cls,name,bases,update_attrs)

    def __call__(self, *args, **kwargs):
        if args:
            raise TypeError('must use keyword argument for key function')
        obj = object.__new__(self) #建立对象,self为类Foo

        for k,v in kwargs.items():
            obj.__dict__[k.upper()]=v
        return obj

class Chinese(metaclass=Mymetaclass):
    country='China'
    tag='Legend of the Dragon' #龙的传人
    def walk(self):
        print('%s is walking' %self.name)


p=Chinese(name='egon',age=18,sex='male')
print(p.__dict__)
在元类中控制自定义的类无需__init__方法
class Mymeta(type):
    def __init__(self,class_name,class_bases,class_dic):
        #控制类Foo的建立
        super(Mymeta,self).__init__(class_name,class_bases,class_dic)

    def __call__(self, *args, **kwargs):
        #控制Foo的调用过程,即Foo对象的产生过程
        obj = self.__new__(self)
        self.__init__(obj, *args, **kwargs)
        obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}

        return obj

class Foo(object,metaclass=Mymeta):  # Foo=Mymeta(...)
    def __init__(self, name, age,sex):
        self.name=name
        self.age=age
        self.sex=sex


obj=Foo('egon',18,'male')
print(obj.__dict__)
在元类中控制自定义的类产生的对象相关的属性所有为隐藏属性
#步骤五:基于元类实现单例模式
# 单例:即单个实例,指的是同一个类实例化屡次的结果指向同一个对象,用于节省内存空间
# 若是咱们从配置文件中读取配置来进行实例化,在配置相同的状况下,就不必重复产生对象浪费内存了
#settings.py文件内容以下
HOST='1.1.1.1'
PORT=3306

#方式一:定义一个类方法实现单例模式
import settings

class Mysql:
    __instance=None
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @classmethod
    def singleton(cls):
        if not cls.__instance:
            cls.__instance=cls(settings.HOST,settings.PORT)
        return cls.__instance


obj1=Mysql('1.1.1.2',3306)
obj2=Mysql('1.1.1.3',3307)
print(obj1 is obj2) #False

obj3=Mysql.singleton()
obj4=Mysql.singleton()
print(obj3 is obj4) #True



#方式二:定制元类实现单例模式
import settings

class Mymeta(type):
    def __init__(self,name,bases,dic): #定义类Mysql时就触发

        # 事先先从配置文件中取配置来造一个Mysql的实例出来
        self.__instance = object.__new__(self)  # 产生对象
        self.__init__(self.__instance, settings.HOST, settings.PORT)  # 初始化对象
        # 上述两步能够合成下面一步
        # self.__instance=super().__call__(*args,**kwargs)


        super().__init__(name,bases,dic)

    def __call__(self, *args, **kwargs): #Mysql(...)时触发
        if args or kwargs: # args或kwargs内有值
            obj=object.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj

        return self.__instance




class Mysql(metaclass=Mymeta):
    def __init__(self,host,port):
        self.host=host
        self.port=port



obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,全部的实例应该指向一个内存地址
obj2=Mysql()
obj3=Mysql()

print(obj1 is obj2 is obj3)

obj4=Mysql('1.1.1.4',3307)



#方式三:定义一个装饰器实现单例模式
import settings

def singleton(cls): #cls=Mysql
    _instance=cls(settings.HOST,settings.PORT)

    def wrapper(*args,**kwargs):
        if args or kwargs:
            obj=cls(*args,**kwargs)
            return obj
        return _instance

    return wrapper


@singleton # Mysql=singleton(Mysql)
class Mysql:
    def __init__(self,host,port):
        self.host=host
        self.port=port

obj1=Mysql()
obj2=Mysql()
obj3=Mysql()
print(obj1 is obj2 is obj3) #True

obj4=Mysql('1.1.1.3',3307)
obj5=Mysql('1.1.1.4',3308)
print(obj3 is obj4) #False
基于元类实现单例模式
相关文章
相关标签/搜索