类 进阶

Angry smile

isinstance 和 issubclass

通过例子看:

isinstance

class Foo(object):
    def __init__(self,name,sex):
        self.name=name
        self.sex=sex
    def printer(self):
        print('kkkkkkkkkkkk')
f1=Foo('abc','male')
print(isinstance(f1,Foo))#f1是不是Foo的对象

issubclass

class Foo(object):
    pass
class Bar(Foo):
    pass
print(issubclass(Bar,Foo))  # Bar 是不是Foo 的子类

 

#反射 Punch

类有属性,对象有属性,只要是通过字符获取的都是反射,在python面向对象中的反射:通过字符的形式操作对象相关的属性python的一切事物都是对象

>>> class Foo(object):
    def __init__(self,name,sex):
        self.name=name
        self.sex=sex
    def printer(self):
        print('kkkkk')

>>> f1=Foo
>>> hasattr(f1,'name')  #判断 f1 对象中有没有‘name’这个方法或属性
True

getattr

>>> getattr(f1,'sex')    #获取  f1 对象中的‘sex’ 属性或方法
'male'

>>> getattr(f1,'abcd')
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    getattr(f1,'abcd')
AttributeError: 'Foo' object has no attribute 'abcd'

setattr

>>> setattr(f1,'name','david')    #设置   f1  对象中  'name'  方法或属性的值,有则更改为‘david’   ,没有则添加至__dict__字典中
>>> print(f1.name)
david
>>> setattr(f1,'age',32)
>>> print(f1.age)
32
>>> print(f1.__dict__)     # 查看对象的数据属性存放
{'age': 32, 'sex': 'male', 'name': 'david'}

delattr

>>> delattr(f1,'age')   #删除对象的属性值
>>> print(f1.__dict__)
{'sex': 'male', 'name': 'david'}
>>> delattr(f1,'sex')
>>> print(f1.__dict__)
{'name': 'david'}

 

反射当前模块属性:

反射当前模块的属性:
先定义一个类
>>>  class test:
    name='tony'
    sex='man'
    def __init__(self):
        print('hello')
    def priter(self):
        print('this printer %s'%self.sex)
        print(self.name)
>>> this_module=sys.mod 
>>> this_module=sys.modules[__name__]      #这个 __name__  说的是这个模块唯一标识导入系统中的模块



>>> print(this_module)
<module '__main__' (built-in)>
>>> hasattr(test,'__init__')        #相当于  '__init__'    in  test.__dict__    ,hasattr 做的就是这个事情 
True
>>> p=test()
hello
>>> setattr
<built-in function setattr>   #修改其属性
>>> setattr(p,'name','liang')  
>>> print(p.name)
liang

 

 

反射的用途:

#两个程序员写一个FTP,Tony,Hua,Tony写程序有些需要用到Hua 所写的类, ,但是Hua 因为有事外出,没有完成要写的类,Tony想到了反射可以继续完成自己的代码,等Hua回来再继续完成类的定义,并且去实现Tony想要的功能。
#反射的好处就是,可以事先定义好接口,接口只有在完成后才会真正执行,这就实现了‘即插即用’,这就是一种‘后期绑定’,就是可以事先把主要的逻辑写好(只定义接口),然后再去实现接口的功能
 
两个文件,一个是ftpserver, 一个是ftpclient ,tony负责Client端的编写,Hua负责Server端的编写
tony没有写完
class FtpCli:
    def __init__(self,addr):
        print('正在连接服务器 [%s]' % addr)
        self.addr=addr
 
Hua可以判断tony客户端写了没有,这就用到了反射,如果写了就执行那一部分的代码,如果没有就继续写其它模块:
import FtpClient
f1=FtpClient.FtpCli('192.168.1.1')
# print(f1)
if hasattr(f1,'get'):  #判断 tony的代码中有没有写get功能。 有则获取运行
    func_get=getattr(f1,'get')
    func_get()
else:                 #没有则继续运行其它的代码
    print('other runing')
 
实际应用:二次加工(包装)
#基于继承的原理来定制自己的数据类型
利用反射定制自己的数据类型,如列表、字典,我们可以定制‘值’ 必须是字符串,如:数据库的ID号,必须是int型。那这个时个可以用到“反射”
>>> class List(list):    #定义列表类,继承 python 的list   
    def append(self,value):    #重写它的append方法 
        if not isinstance(value,int):  #判断输入的值是不是 int类型
            raise TypeError('Must be int')   #不是int类型,则举出 ‘必须是int’  的错误
        else:
            super().append(value)   #如果是整型,则使用超类(list)的append方法添加值到列表。    自定义的类是没有append 这些方法的


>>> l1=List([1,2,2,3,4,5,])   #实例化List 类
[1, 2, 2, 3, 4, 5]
>>> l1.append(0)
[1, 2, 2, 3, 4, 5, 0]
>>> l1.append('3') 
Traceback (most recent call last):
  File "<pyshell#106>", line 1, in <module>
    l1.append('3')
  File "<pyshell#100>", line 4, in append
    raise TypeError('Must be int')
TypeError: Must be int    
>>>
 
上面的都理解没有问题:
>>> l1.insert(0,'fda')    这又是怎么回事呢?   这里的insert 不是我们自己定义的List 中的 insert  ,因为没有经过自己定制,那l1就自己执行超类的 insert 方法了。  除非,自己再定制一个 insert  ,再进行一次判断,不能insert 字符串。
>>> l1
['fda', 1, 2, 2, 3, 4, 5, 0]
 

 

授权:

不能用继承,实现字典的功能: 字典的值只能是str类型:

#不使用继承  完成字典的数据定制,字典的值必须是int ,

class Dict:
    def __init__(self,key,value):      # 设置值:字典只有key和value
        if not isinstance(value,int):        # 设置 字典的值只能是int 类型
            raise TypeError('must be int')  # 不是int 类型则报出错误
        else:
            self.key=key  #如果是字典类型 则把key的值传进来
        self.c = {key: value} # 初始一个字典,这样就可以限制  健和值了
        self.value = value  #值 赋给self.value
    def fu_oprate(self,key,value):     #字典操作 ,也是有键和值
        if not isinstance(value,int):  # 条件判断 传进来的值只能是int ,不是整型则报出错误
            raise TypeError('must be int')  # 报出错误
        if not isinstance(key,str):   #条件判断‘键’ ,只能是字符串类型
            raise TypeError('must be str') #不是字符串类型,报错
        else:
            self.c[key]=value   #如果上述条件都成立,那就将  '值' 赋给‘键’
            return self.c  #返回整个字典
di1=Dict('name',‘tony’)  #实例化 Dict
print(di1.fu_oprate('age','fifteen'))  # 操作字典,这里就已经限制了‘值’的数据类型,这就已经完成自定义数据类型的定制

 

__getattribute__

回顾__getattr___

>>> class Foo:
    def __init__(self,x):
        self.x=x      #print(f1,x) 执行的是这里,(过程是先在f1里面找, f1里没有就找类的f1)
    def __getattr__(self,item):   
        print('exe is me')   #f1.xxxxxxx 执行的是这里   ,

>>> f1=Foo(10)  #实例化Foo(10)  并传入值
>>> print(f1.x)
10
>>> print(f1.__dict__)   # 自己的属性值存放的名称空间。
{'x': 10}
>>> f1.xxxxxxx   #不存在的属性访问,触发__getattr__ 
exe is me

__getattribute__

 

再看一个例子:

>>> class Foo:
    def __init__(self,x):
        self.x=x
    def __getattr__(self,item):
        print('执行的是我')
    def __getattribute__(self,item):
        print('不管是否存在,我都会执行')
        raise AttributeError('lalalalalalalalalalala')

    
>>> f1=Foo(100)
>>> f1.x
不管是否存在,我都会执行
执行的是我
>>> f1.xxxxxxxxxx
不管是否存在,我都会执行
执行的是我
>>> print(f1.x)
不管是否存在,我都会执行
执行的是我
None

#结论: 当__getattribute__与  __getattr__同时存在,只会执行__getattribute__,除非__getattribute__在执行过程中抛出异常AttributeError

 

描述符(__get__ , __set__ , __delete__)

1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(), __set__() ,__delete__()中的一个,这也被称为描述符协议,

__get__():调用 一个属性时会触发

__set__():为一个属性赋值时会触发

__delete__(): 采用del 删除属性时会触发

 

定义一个描述符:

class Foo:
    def __get__(self,instance,owner):
        pass
    def __set__(self,instance,value):
        pass
    def __delete__(self,instance):
        pass

描述符是干什么的,描述符的作用是用来代理另外一个类的属性(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性  调用/赋值/删除/    并不会触发这三个方法
 class descrip:
    def __get__(self,instance,owner):
        print('trigger get')
    def __set__(self,instance,value):
        print('trigger set')
    def __delete__(self,instance,value):
        print('trigger delete ')

>>> F1=descrip()
>>> F1.name='tony'
>>> F1.name
'tony'
>>> del F1.name

#以上都没有触发其中的方法,那什么时候才会触发 这三个方法?

 

描述符应用于何时?何地?

>>> class Str:
    def __get__(self,instance,owner):
        print('Str call')
    def __set__(self,instance,value):
        print('Str set.....')
    def __delete__(self,instance):
        print('Str delete')

        
>>> class Int:
    def __get__(self,instance,owner):
        print('Int call')
    def __set__(self,instance,value):
        print('Ins set....')
    def __delete__(self,instance):
        print('Int delete....')

        
>>> class human:
    name=Str()
    age=Int()
    def __init__(self,name,age):
        self,name=name
        self.age=age

#何时运行

>>> h1=human('tony',23)    #实例化操作触发了两个类    set   的运行
Str set.....
Ins set....

描述符Str  的使用:
>>> h1.name
Str call
>>> h1.name='liang'
Str set.....
>>> del h1.name
Str delete


#描述符 Int 的使用:
>>> h1.age
Int call
>>> h1.age=20
Ins set....
>>> del h1.age
Int delete....

#查看一下类和对象的名称空间  
>>> h1=human('tony',23)
Str set.....
Ins set....
>>> print(human.__dict__)
{'__module__': '__main__', 'name': <__main__.Str object at 0x0000000003684160>, '__weakref__': <attribute '__weakref__' of 'human' objects>, '__dict__': <attribute '__dict__' of 'human' objects>, '__init__': <function human.__init__ at 0x000000000367DAE8>, '__doc__': None, 'age': <__main__.Int object at 0x0000000003684320>}
>>> print(h1.__dict__)  #对象中的dict是空的 ,说明都是值都是存放在类里的。
{}
>>>  

#####################################################################################################
>>> print(type(h1))
<class '__main__.human'>
>>> print(type(human))
<class 'type'>
>>> print(type(h1).__dict__ == human.__dict__)    #对象 h1明明是空的 ,为何等于human.__dict__是True ,因为human 是由type 产生的。而h1 由human实例化得来
True
>>>

 

描述符分两种:

一:数据描述符:至少实现了__get__() 和 __set__()

>>> class Test:
    def __set__(self,instance,value):
        print('set')
    def __get__(self,instance,owner):
        print('get')

二:非数据描述符:没有实现__set__()

>>> class Test:
    def __get__(self,instance,owner):
        print('get')

 

#####

注意:

一、描述符本身应该定义成新式类,被代理的类也应该是新式类(python3 都是新式类)

二、必须把描述符定义成这个类的类属性,不能定义到构造函数中

三、要严格遵循该优先级,优先级由高到低 分别是

1、类属性

2、数据描述符

3、实例属性

4、非数据描述符

5、找不到属性触发__getattr__()

类属性 高于 数据描述符

>>> class Str:
    def __get__(self,instance,owner):
        print('Str call')
    def __set__(self,instance,value):
        print('Str set....')
    def __delete__(sel,instance):
        print('Str delete.....')

>>> class Human:        
    name=Str()
    def __init__(self,name,age):    #name被Str类代理,age被Int 类代理
        self.name=name
        self.age=age
#在类中定义描述符,它就是一个类属性,存在于类的属性字典中,而不是实例属性字典
#那既然描述符被定义成了一个类属性,直接通过类名也可以调用。

>>> Human.name  #,调用类属性name,本质就是在调用描述符Str,触发了__get__()
Str call
>>> Human.name='tony'   #赋值为什么没有触发 __get__()
>>> del Human.name    #   删除也没有触发。  

 >>>   这是因为优先级的原因。类属性赋值 高于描述符的 级别。所以没有触发。

 

数据描述符 高于 实例属性

>>> class Str:
    def __get__(self,instance,owner):
        print('Str call')
    def __set__(self,instance,value):
        print('Str set....')
    def __delete__(sel,instance):
        print('Str delete.....')

>>> class Human:
    name=Str()
    def __init__(self,name,age):
        self.name=name
        self.age=age

        
>>> Human.name
Str call
>>> Human.name='tony'
>>> del Human.name
>>> 
>>> h2=human('tonyyy',20)
Str set.....
Ins set....

##如果描述符是一个数据描述符(即有__get__又有__set__),那么h2.name的调用与赋值都是触发描述符的操作,与h2本身无关了,相当于覆盖了实例的属性
>>> h2.name='tonyyyyyyyyyyyyyyyyyyyyyyy'
Str set.....
>>> h2.name
Str call
>>> print(h2.__dict__)    #实例的属性字典中没有name ,因为name是一个数据描述符,优先级高于实例属性,查看\赋值\删除\ 都是跟描述符有关,与实例无关
{}

>>> del h2.age
Int delete....
>>>

实例属性 高于 非数据描述符

>>> class test:
    def func(self):
        print('I\'m back')

        
>>> t1=test()
>>> t1.func()    #调用类的方法,也可以说是调用非数据描述符
#函数是一个非数据描述符对象(一切皆对象)
I'm back
>>> 
>>> print(dir(test.func))
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> print(hasattr(test.func,'__set__'))
False
>>> print(hasattr(test.func,'__get__'))
True
>>> print(hasattr(test.func,'__delete__'))
False


描述符在应用的时候都是实例成一个类属性,函数就是一个由非描述符类实例化得到的对象,字符串也是。

>>> t1.func='this instance attr'
>>> print(t1.func)
this instance attr

>>> del t1.func     #删除掉了非数据
>>> t1.func()
I'm back

 

 

非数据描述符 高于 找不到

>>> class test:
    def func(self):
        print('I\'m back agin')
    def __getattr__(self,item):
        print('not found ,of couse find me ',item)

        
>>> t1=test()
>>> t1.xxxxxxxxxxx
not found ,of couse find me  xxxxxxxxxxx   #找不到,不会报错,而是先找实例的__getattr__属性
>>> t1.func
<bound method test.func of <__main__.test object at 0x00000000036849B0>>
>>> t1.func()       # 
I'm back agin
>>>

 

__setitem__ __getitem__ __delitem__

>>> class test:
    def __init__(self,name):
        self.name=name
    def __getitem__(self,item):
        print(self.__dict__[item])
    def __setitem__(self,key,value):
        self.__dict__[key]=value
    def __delitem__(self,key):
        print('del obj[key] i\'m running')
        self.__dict__.pop(key)
    def __delattr__(self,item):
        print('del obj.key , i\'m running')
        self.__dict__.pop(item)

        
>>> t1=test('liang')    
>>> t1['age']=18 
>>> t1['age2']=23 
>>> del t1.age1 
del obj.key , i'm running  #这里触发了__delattr__(self,item):
Traceback (most recent call last): 
  File "<pyshell#249>", line 1, in <module>  
    del t1.age1  
  File "<pyshell#245>", line 13, in __delattr__
    self.__dict__.pop(item)
KeyError: 'age1'
>>> del t1['age']      
del obj[key] i'm running    #这里触发 __delitem__(self,key):
>>> t1['name']='tony'
>>> print(t1.__dict__)
{'name': 'tony', 'age2': 23}
>>>

 

___slots__

直接看效果,__slots__  可以节省内存, 如下,指定的属性可以使用,没有指定则报出没有属性
目的可以节省内存,如果是字典的话,来一个对象都会创建一个名称空间。

>>> class test:
    __slots__='x'
>>> t1=test()
>>> t1.x=1
>>> t1.y=2
Traceback (most recent call last):
  File "<pyshell#271>", line 1, in <module>
    t1.y=2
AttributeError: 'test' object has no attribute 'y'

同时对象也没有__dict__
>>> print(f1.__slots__)
Traceback (most recent call last):
  File "<pyshell#272>", line 1, in <module>
    print(f1.__slots__)
AttributeError: 'Foo' object has no attribute '__slots__'

再来一个例子:

>>> class Bar:
    __slots__=['x','y']

    
>>> n=Bar()
>>> n.x,n.y=1,2
>>> n.z
Traceback (most recent call last):
  File "<pyshell#278>", line 1, in <module>
    n.z
AttributeError: 'Bar' object has no attribute 'z'
>>> n.x
1
>>> n.y
2
>>>

 

再看:

>>> class test:
    __slots__=['name','age']

    
>>> t1=test()
>>> t1.name
Traceback (most recent call last):
  File "<pyshell#285>", line 1, in <module>
    t1.name
AttributeError: name
>>> t1.name='tony'
>>> t1.age=29

>>> print(t1.__slots__)
['name', 'age']
>>> t2=test()
>>> t2.name='liang'
>>> t2.age=22
>>> print(t2.__dict__)      #对象中已经没有属性字典__dict__,统一归 __slots__管,节省内存
Traceback (most recent call last):
  File "<pyshell#293>", line 1, in <module>
    print(t2.__dict__)
AttributeError: 'test' object has no attribute '__dict__'
>>> print(test.__dict__)
{'__module__': '__main__', 'name': <member 'name' of 'test' objects>, '__slots__': ['name', 'age'], '__doc__': None, 'age': <member 'age' of 'test' objects>}
>>>

 

__next__ 和__iter__实现迭代器协议:

如果有 __next__ 方法在的话,这个方法会自动的next到下一个,直到碰到StopIteration,但是 for 可以 自处理这个StopIteration

>>> class test:  #实现迭代器

    def __init__(self,x):
        self.x=x
    def __iter__(self):
        return self
    def __next__(self):
        n=self.x
        self.x+=1
        return self.x

    
>>> t1=test(0)
>>> for i in t1:
    print(i)
    
KeyboardInterrupt
>>> 
>>> for i in t1:
    print(i)

1
2
3
.
.
.
.
.
.

.
.
.
.
.
.
..
.
.
.

 

自定义一个range

range 有一个开始值和结尾值。

>>> class test:
    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

    
>>> t1=test(0,5)
>>> from collections import Iterable,Iterator
>>> print(isinstance(t1,Iterator))
True
>>> for i in test(0,5):
    print(i)

    
0
1
2
3
4

 

Fibonacci 数列

>>> 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(next(f1))
1
>>> print(next(f1))
1
>>> print(next(f1))
2
>>> print(next(f1))
3
>>> print(next(f1))
5
>>> print(next(f1))
8
>>> for i in f1:
    if i>100:
        break
    print(i)

    
13
21
34
55
89

 

__doc__ 在编写代码强制写上‘描述信息’ ,养成良好的编程习惯

 

>>> class test:
    '''I"m description'''
    pass

>>> print(test.__doc__)   #通过类名.点  __doc__ 查看描述信息
I"m description
>>>

__doc__ 无法被子类继承

>>> class test:
    'i\'m description'
    pass

>>> class Bar(test):
    pass

>>> print(Bar.__doc)       #无法继承该属性
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    print(Bar.__doc)
AttributeError: type object 'Bar' has no attribute '__doc'
>>>

 

 

 

__module__ 和 __class__

 

 

 

 

完工~~~~~~  Be right back  Be right back Be right back Be right back Be right back Be right back Be right back Be right back Be right back Be right back Be right back Be right back Be right back Be right back

posted @ 2017-04-25 23:13  tonycloud  阅读(192)  评论(0)    收藏  举报