28. 面向对象进阶之内置方法上

 

 

vars()

1、vars()内建函数与 dir()相似,只是给定的对象参数都必须有一个__dict__属性

2、vars()返回一个字典,它包含了对象存储于其__dict__中的属性(键)及值。

3、如果提供的对象没有这样一个属性,则会引发一个 TypeError 异常。如果没有提供对象作为 vars()的一个参数,它将显示一个包含本地名字空间的属性(键)及其值的字典, 也就是, locals()

 

使用类实例调用 vars():

 

#!/usr/bin/env python
#coding:utf8

class C(object):
    pass
c = C()
c.foo = 100
c.bar = 'Python'
print c.__dict__
print vars(c)

 

执行结果:

{'foo': 100, 'bar': 'Python'}
{'foo': 100, 'bar': 'Python'}

 

dir()

1.用 dir()列出一个模块所有属性的信息

2.dir()还可以用在对象上

 

  dir()作用在实例上(经典类或新式类)时,显示实例变量,还有在实例所在的类及所有它的基类中定义的方法和类属性。
  dir()作用在类上 (经典类或新式类) 时, 则显示类以及它的所有基类的__dict__中的内容。但它不会显示定义在元类(metaclass)中的类属性。
  dir()作用在模块上时,则显示模块的__dict__的内容。(这没改动)。
  dir()不带参数时,则显示调用者的局部变量。(也没改动)。

 

关于更多细节:对于那些覆盖了__dict__或__class__属性的对象,就使用它们;出于向后兼容的考虑,如果已定义了__members__和__methods__,则使用它们。

 

super()

1.super() 函数是用于调用父类(超类)的一个方法。

2.super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

3.MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

4、super的语法:

super(type[, object-or-type])

 

Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :

Python3.x 实例:

class A:
    pass
class B(A):
    def add(self, x):
        super().add(x)

 

 Python2.x 实例:

class A(object):   # Python2.x 记得继承 object
    pass
class B(A):
    def add(self, x):
        super(B, self).add(x)

 

有以下4个点值得大家注意:

1、单继承时super()和__init__()实现的功能是类似的

#!/usr/bin/env python
#coding:utf8

class Base(object):
    def __init__(self):
        print 'Base create'

class childA(Base):
    def __init__(self):
        print 'creat A ',
        Base.__init__(self)


class childB(Base):
    def __init__(self):
        print 'creat B ',
        super(childB, self).__init__()

base = Base()

a = childA()
b = childB()

 

执行结果:

Base create
creat A  Base create
creat B  Base create

使用super()继承时不用显式引用基类。

 

2、super()只能用于新式类中

把基类改为旧式类,即不继承任何基类

#!/usr/bin/env python
#coding:utf8

class Base:
    def __init__(self):
        print 'Base create'

class childA(Base):
    def __init__(self):
        print 'creat A ',
        Base.__init__(self)


class childB(Base):
    def __init__(self):
        print 'creat B ',
        super(childB, self).__init__()

base = Base()

a = childA()
b = childB()

 

执行时,在初始化b时就会报错:

super(childB, self).__init__()
TypeError: super() argument 1 must be type, not classobj

 

3.super不是父类,而是继承顺序 MRO 的下一个类

忘记了这件事之后,再去看这篇文章:Python’s super() considered super! 这是 Raymond Hettinger 写的一篇文章,也是全世界公认的对 super 讲解最透彻的一篇文章,凡是讨论 super 都一定会提到它(当然还有一篇 Python's Super Considered Harmful)。

在多重继承时会涉及继承顺序,super()相当于返回继承顺序的下一个类,而不是父类,类似于这样的功能:

 

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

两个参数 cls 和 inst 分别做了两件事:
1. inst 负责生成 MRO 的 list
2. 通过 cls 定位当前 MRO 中的 index, 并返回 mro[index + 1]
这两件事才是 super 的实质,一定要记住!
MRO 全称 Method Resolution Order,它代表了类继承的顺序

 

 

例如:

#!/usr/bin/env python
#coding:utf8

class Base(object):
    def __init__(self):
        print 'Base create'

class childA(Base):
    def __init__(self):
        print 'enter A '
        # Base.__init__(self)
        super(childA, self).__init__()
        print 'leave A'


class childB(Base):
    def __init__(self):
        print 'enter B '
        # Base.__init__(self)
        super(childB, self).__init__()
        print 'leave B'

class childC(childA, childB):
  pass

c = childC()
print c.__class__.__mro__

 

执行结果:

enter A 
enter B 
Base create
leave B
leave A
(<class '__main__.childC'>, <class '__main__.childA'>, <class '__main__.childB'>, <class '__main__.Base'>, <type 'object'>)

supder和父类没有关联,因此执行顺序是A —> B—>—>Base

   

执行过程相当于:初始化childC()时,先会去调用childA的构造方法中的 super(childA, self).__init__(), super(childA, self)返回当前类的继承顺序中childA后的一个类childB;然后再执行childB().__init()__,这样顺序执行下去。

 在多重继承里,如果把childA()中的 super(childA, self).__init__() 换成Base.__init__(self),在执行时,继承childA后就会直接跳到Base类里,而略过了childB:

enter A 
Base create
leave A
(<class '__main__.childC'>, <class '__main__.childA'>, <class '__main__.childB'>, <class '__main__.Base'>, <type 'object'>)

 

从super()方法可以看出,super()的第一个参数可以是继承链中任意一个类的名字,

    如果是本身就会依次继承下一个类;

    如果是继承链里之前的类便会无限递归下去;

    如果是继承链里之后的类便会忽略继承链汇总本身和传入类之间的类;

    比如将childA()中的super改为:super(childC, self).__init__(),程序就会无限递归下去。

 

 

MRO 中类的顺序到底是怎么排的呢?

在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。

 

4、super()可以避免重复调用

  如果childA基础Base, childB继承childA和Base,如果childB需要调用Base的__init__()方法时,就会导致__init__()被执行两次:

 

例子1:#Base的__init__()方法被执行了两次

#!/usr/bin/env python
#coding:utf8
class Base(object):
  def __init__(self):
    print 'Base create'

class childA(Base):
  def __init__(self):
    print 'enter A '
    Base.__init__(self)
    print 'leave A'


class childB(childA, Base):
  def __init__(self):
    childA.__init__(self)
    Base.__init__(self)

b = childB()
#Base的__init__()方法被执行了两次

 

 执行结果:

enter A 
Base create
leave A
Base create

 

 例子2:使用super()是可避免重复调用

#!/usr/bin/env python
#coding:utf8

class Base(object):
  def __init__(self):
    print 'Base create'

class childA(Base):
  def __init__(self):
    print 'enter A '
    super(childA, self).__init__()
    print 'leave A'


class childB(childA, Base):
  def __init__(self):
    super(childB, self).__init__()

b = childB()
print b.__class__.mro()

执行结果:

enter A 
Base create
leave A
[<class '__main__.childB'>, <class '__main__.childA'>, <class '__main__.Base'>, <type 'object'>]

 

 

 

 

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

 

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

例子:

 

#!/usr/bin/env python
#coding:utf8

class Foo(object):
    pass
obj = Foo()
print isinstance(obj, Foo) #True

 

issubclass(sub, super)检查sub类是否是 super 类的派生类

 

 

#!/usr/bin/env python
#coding:utf8

class Foo(object):
    pass
class Bar(Foo):
    pass
print issubclass(Bar, Foo)  #True

 

issubclass和isinstance

 

#_*_coding:utf-8_*_
 
class A:
    pass
 
class B(A):
    pass
 
print(issubclass(B,A)) #B是A的子类,返回True
 
a1=A()
print(isinstance(a1,A)) #a1是A的实例

 

二 反射

 

1.什么是反射

 

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

 

2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

 

 

4个可以实现自省的函数

下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

 

hasattr(object,name) 判断object中有没有对应的方法和属性

 

判断object中有没有一个name字符串对应的方法或属性

 

#!/usr/bin/env python
#coding:utf8

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def talk(self):
        print '%s is talking' % self.name
obj
= People('huard',18) print hasattr(obj,'name') print hasattr(obj,'talk') print hasattr(obj,'age') # 结果: # True # True # True

 

 

getattr(object, name, default=None)  获取object中有没有对应的方法和属性

这个函数作用是返回对象的一个属性和方法,第一个参数是对象实例object,name是个字符串,是对象的成员函数名字或者成员变量,default当对象没有这个属性和方法的时候就返回默认值,如果没有提供默认值就返回异常。

 

语法:

getattr(...)
    getattr(object, name[, default]) -> value
    
    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.

 

 

例子:

#!/usr/bin/env python
#coding:utf8

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def talk(self):
        print '%s is talking' % self.name
obj = People('huard',18)
print getattr(obj,'name')  #获取name属性,存在就打印出来
print getattr(obj,'talk')  #获取talk方法,存在就打印出方法的内存地址。
print getattr(obj,'talk')()  #获取talk方法,后面加括号可以将这个方法运行。
print getattr(obj,'age',None)    #获取age属性,存在就打印出来,否则返回一个默认值
print getattr(obj,'ads',None)    #获取ads属性,存在就打印出来,否则返回一个默认值
# 结果:(如果有的话 就返回值,没有的话就返回None)
# huard
# <bound method People.talk of <__main__.People object at 0x000001661CE1CBA8>>
# huard is talking
# 18
# None

 

 

 

setattr(x, y, v) 设置对象及其属性

 

给对象的属性赋值,若属性不存在,先创建再赋值。

 

#!/usr/bin/env python
#coding:utf8

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def talk(self):
        print '%s is talking'%self.name
obj = People('huard',18)
setattr(obj,'sex','male')
print obj.__dict__
print obj.sex
# 结果:
# {'name': 'huard', 'age': 18, 'sex': 'male'}
# male

 

 

delattr(x, y) 删除类或对象的属性

例子:

 

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def talk(self):
        print '%s is talking' % self.name
 
obj = People('huard', 18)
delattr(obj, 'age')
print obj.__dict__
# 结果:
# {'name': 'huard'}

 

 

四个方法的使用演示

 

class BlackMedium:
    feature='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr
 
    def sell_house(self):
        print('%s 黑中介卖房子啦,,但是谁能证明自己不mai' %self.name)
    def rent_house(self):
        print('%s 黑中介租房子啦,鬼才租呢' %self.name)
 
b1=BlackMedium('万成置地','回龙观天露园')
 
#检测是否含有某属性
print(hasattr(b1,'name'))   #True
print(hasattr(b1,'sell_house')) #True
 
#获取属性
n=getattr(b1,'name')
print(n)  #万成置地
func=getattr(b1,'rent_house')
func()   #万成置地 黑中介租房子啦,才租呢
 
# getattr(b1,'aaaaaaaa') #报错
'''    getattr(b1,'aaaaaaaa') #报错
AttributeError: 'BlackMedium' object has no attribute 'aaaaaaaa'
'''
#为了不让报错,我们提前设置异常处理,如果没有的话 直接读取的是我们设置的
print(getattr(b1,'aaaaaaaa','不存在啊'))  #不存在啊
 
#设置属性
setattr(b1,'sb',True)
setattr(b1,'show_name',lambda self:self.name+'sb')
print(b1.__dict__)
# {'name': '万成置地', 'addr': '回龙观天露园', 'sb': True, 'show_name': <function <lambda> at 0x000001A26A0E56A8>}
print(b1.show_name(b1))
# 万成置地sb
 
#删除属性
delattr(b1,'addr')
delattr(b1,'show_name')
# delattr(b1,'show_name111')#不存在,则报错AttributeError: show_name111
 
print(b1.__dict__) #{'name': '万成置地', 'sb': True}

 

再看一个例子:

 

class Game:
    __game_type = 'cross_f'
    def __init__(self,g_name,player,gun,moeny):
        self.g_name = g_name
        self.g_player = player
        self.g_gun = gun
        self.g_moeny = moeny
    def buy_gun(self):
        print('%s买了一把%s游戏的%s并花费了%s元'%(self.g_player,self.g_name,self.g_gun,self.g_moeny))
g1 = Game('cf','zk','blt',100)

#hasattr 判断object中有没有一个name字符串对应的方法或属性
print(hasattr(g1,'name'))     #True或者False

#getattr 用于以字符串的形式去传入对象的字典中寻找函数
a = getattr(g1,'g_name1','随便定义')#语法getattr(对象,'字符串名',默认参数任意)    #监测g1可不可以调取到某个数据属性和函数属性
print(a)    #如果存在,数据属性,返回对应的V值,函数返回对应的函数对象,不存在返回设置的默认参数
#执行结果:随便定义

g1.g_test = 888 #添加函数和覆盖函数
#setattr 如果该值存在,就覆盖,不存在就添加
setattr(g1,'g_name','aaa')  #添加函数和覆盖函数,执行效果和g1.g_test = 888一样
setattr(g1,'g_sex','aaa')  #添加函数和覆盖函数,执行效果和g1.g_sex = aaa一样
print(g1.__dict__)
#执行结果:{'g_name': 'aaa', 'g_moeny': 100, 'g_sex': 'aaa', 'g_gun': 'blt', 'g_test': 888, 'g_player': 'zk'}


del g1.g_test
#delattr 如果该值存在就删除,不存在报错
delattr(g1,'g_name')              #AttributeError: name
print(g1.__dict__)
#执行结果:{'g_moeny': 100, 'g_player': 'zk', 'g_gun': 'blt', 'g_sex': 'aaa'}

 

 

类也是对象

 

#!/usr/bin/env python
#coding:utf8

class Foo(object):
    staticField = "hello kitty"

    def __init__(self):
        self.name = 'panda'

    def func(self):
        return 'func'

    @staticmethod
    def bar():
        return 'bar'


print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))


 

执行结果:

#python2:
hello kitty
<unbound method Foo.func>
<function bar at 0x00000000033E8048>

#python 3

hello kitty
<function Foo.func at 0x0000019BBB7FB950>
<function Foo.bar at 0x0000019BBB7FB9D8>

 

 

 

反射当前模块成员

例子:

 

#!/usr/bin/env python
#coding:utf8



import sys


def s1():
    print 's1'


def s2():
    print 's2'


this_module = sys.modules[__name__]    #利用sys模块的modules方法将当前模块形成一个对象

print hasattr(this_module, 's1')   
print getattr(this_module, 's2')

#结果
#True
#<function s2 at 0x0000000002721F98>

 

例子2:

#!/usr/bin/env python
#coding:utf8

import sys
def s1():
    print('s1')
def s2():
    print ('s2')
this_module = sys.modules['__main__']   #__main__表示当前文件
print hasattr(this_module, 's1')   #判断's1'是否在当前文件的方法名中
getattr(this_module, 's2')()    #取出

#结果
#True
#s2

 

通过__import__导入模块

导入其他模块,利用反射查找该模块是否存在某个方法

 

import module_test as obj
 
#obj.test()
 
print(hasattr(obj,'test'))
 
getattr(obj,'test')()
# True
# from the test

 

module_test.py

 

# _*_ coding: utf-8 _*_
def test():
    print('from the test')

 

 

 

 

 

为什么用反射?(反射的好处)

好处一:实现可插拔机制

  有俩程序员,一个james,一个是dunart,james在写程序的时候需要用到dunart所写的类,但是dunart去跟女朋友度蜜月去了,还没有完成他写的类,james想到了反射,使用了反射机制james可以继续完成自己的代码,等dunart度蜜月回来后再继续完成类的定义并且去实现james想要的功能。

  总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

 

 dunart还没有实现全部功能

 

lass FtpClient:
    'ftp客户端,但是还么有实现具体的功能'
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr=addr

 

不影响james的代码编写

 

#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get=getattr(f1,'get')
    func_get()
else:
    print('---->不存在此方法')
    print('处理其他的逻辑')

 

好处二:动态导入模块(基于反射当前模块成员)

 

 

 

三、__getattr__, __getattribute__, __setattr__, __delattr__

 

都是给类的实例用的方法:

  __setattr__:添加/修改实例的属性会触发它的执行(不去定义修改的话,默认就是在对象的__dict__里添加/修改属性的操作)
  __delattr__:删除实例的属性会触发它的执行(不去定义修改的话,默认就是在对象的__dict__里删除属性)
  __getattr__:调用实例的属性且属性不存在时才会触发执行(不去定义修改的话,默认就是对象无法调用到属性就报错)

 

 

 

常情况下,我们在访问类或者实例对象的时候,会牵扯到一些属性访问的魔法方法,主要包括:

 

① __getattr__(self, name): 访问不存在的属性时调用

② __getattribute__(self, name):访问存在的属性时调用(先调用该方法,查看是否存在该属性,若不存在,接着去调用①)

③ __setattr__(self, name, value):设置实例对象的一个新的属性时调用

④ __delattr__(self, name):删除一个实例对象的属性时调用

 

调用类的实例的属性时,无论是否找的到该属性,都会触发__getattribute__

若__getattribute__与__getattr__同时存在,当找不到属性时,也只会执行__getattrbute__,除非__getattribute__在执行过程中指定抛出异常AttributeError(必须指定为AttributeError,其他异常会直接中止程序运行),那么该异常就会被屏蔽,且继续运行__getattr__
 
❉注:实际上,系统默认的__getattribute__方法就是在找到实例的属性时,返回这个属性的值,找不到则raise出AttributeError异常,如果我们写了__getattr__方法,则会屏蔽掉该异常,继续运行__getattr__

 

 

为了验证以上,现列出代码如下:

经典类:

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


class Test:
    def __getattr__(self, name):
        print('__getattr__')

    def __getattribute__(self, name):
        print('__getattribute__')

    def __setattr__(self, name, value):
        print('__setattr__')

    def __delattr__(self, name):
        print('__delattr__')

t=Test()
t.x  #__getattr__

 

新式类:

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


class Test(object):
    def __getattr__(self, name):
        print('__getattr__')

    def __getattribute__(self, name):
        print('__getattribute__')

    def __setattr__(self, name, value):
        print('__setattr__')

    def __delattr__(self, name):
        print('__delattr__')

t=Test()
t.x    #__getattribute__

 

如上述代码所示,x并不是Test类实例t的一个属性,首先去调用 __getattribute__() 方法,得知该属性并不属于该实例对象;但是,按照常理,t.x应该打印 __getattribute__ 和__getattr__,但实际情况并非如此,为什么呢?难道以上Python的规定无效吗?

不要着急,听我慢慢道来!

 实例对象属性寻找的顺序如下:

① 首先访问 __getattribute__() 魔法方法(隐含默认调用,无论何种情况,均会调用此方法)

② 去实例对象t中查找是否具备该属性: t.__dict__ 中查找,每个类和实例对象都有一个 __dict__ 的属性

③ 若在 t.__dict__ 中找不到对应的属性, 则去该实例的类中寻找,即 t.__class__.__dict__

④ 若在实例的类中也招不到该属性,则去父类中寻找,即 t.__class__.__bases__.__dict__中寻找

⑤ 若以上均无法找到,则会调用 __getattr__ 方法,执行内部的命令(若未重载 __getattr__ 方法,则直接报错:AttributeError)

 

以上几个流程,即完成了属性的寻找。

 

但是,以上的说法,并不能解释为什么执行 t.x 时,不打印 '__getattr__'  啊?

你看,又急了不是,作为一名程序猿,一定要有耐心 ^_^

问题就出在了步骤的第④步,因为,一旦重载了 __getattribute__() 方法,如果找不到属性,则必须要手动加入第④步,否则无法进入到 第⑤步 (__getattr__)的。

验证一下以上说法是否正确:

 

方法一:采用object(所有类的基类)

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


class Test(object):
    def __getattr__(self, name):
        print('__getattr__')

    def __getattribute__(self, name):
        print('__getattribute__')
        object.__getattribute__(self, name)

    def __setattr__(self, name, value):
        print('__setattr__')

    def __delattr__(self, name):
        print('__delattr__')

t=Test()
t.x
#__getattribute__
#__getattr__

 

怎么样,显示出来了吧?哈哈

 

方法二:采用 super() 方法

 

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


class Test(object):
    def __getattr__(self, name):
        print('__getattr__')

    def __getattribute__(self, name):
        print('__getattribute__')
        super(Test,self).__getattribute__(name)

    def __setattr__(self, name, value):
        print('__setattr__')

    def __delattr__(self, name):
        print('__delattr__')

t=Test()
t.x
__getattribute__
__getattr__

 

哈哈,酱紫也可以哦 ^v^

那么方法一和方法二有什么不同呢?仔细看一下你就会发现,其实就是很小的一点不同而已:

#方法一:使用基类object的方法
def __getattribute__(self, name):
    print('__getattribute__')
    object.__getattribute__(self, name)

#方法二:使用super()方法(有的认为super()是类,此处暂以方法处理)
def __getattribute__(self, name):
        print('__getattribute__')
       super(Test,self).__getattribute__(name)

 

在Python2.x中,以上super的用法应该改为 super(Test, self).xxx,但3.x中,可以像上面代码一样简单使用。

哈哈,以上介绍完毕,那么 __setattr__ 和 __delattr__ 方法相对简单了多了:

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


class Test(object):
    def __getattr__(self, name):
        print('__getattr__')

    def __getattribute__(self, name):
        print('__getattribute__')
        super(Test,self).__getattribute__(name)

    def __setattr__(self, name, value):
        print('__setattr__')

    def __delattr__(self, name):
        print('__delattr__')

t=Test()
t.x=10  #__setattr__
del t.x  #__delattr__

 

 对了,再补充一点哈

 

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


class Test:
    def __init__(self):
        self.count = 0

    def __setattr__(self, name, value):
        print('__setattr__')
        self.count += 1

t = Test()

 

执行结果:

__setattr__
Traceback (most recent call last):
  File "G:/PycharmProject/test/test/test4.py", line 13, in <module>
    t = Test()
  File "G:/PycharmProject/test/test/test4.py", line 7, in __init__
    self.count = 0
  File "G:/PycharmProject/test/test/test4.py", line 11, in __setattr__
    self.count += 1
AttributeError: Test instance has no attribute 'count'

 

为什么会出现上述情况呢?我还没有调用__setattr__()呢,只是单纯的定义了一个实例而已? @_@(咋回事?幻觉吗)

看报错信息很容易明白,这是因为:

 

① __init__()时,给内部属性 self.count进行了赋值;

② 赋值默认调用 __setattr__() 方法

③ 当调用 __setattr__()方法时,首先打印 '__setattr__'字符串,而后执行 self.cout += 1操作

④ 当执行 self.cout 加 1 操作时,将会去寻找 count 这个属性,然而,由于此时 __init__尚未完成,并不存在 count这个属性,因此导致 'AttributeError' 错误

 

那么该如何更改呢?可以这样的:

 

 

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


class Test(object):
    def __init__(self):
        self.count = 0

    def __setattr__(self, name, value):
        print('__setattr__')
        super(Test,self).__setattr__(name, value + 1)

t = Test()  #__setattr__
print t.count  #1

 

如何,问题解决了吧!

但是以上代码虽然解决了报错的问题,深入体会一下,你会发现,采用此方法只是给 基类object增加了一个属性 count,而并不是实例的属性,因此,以上这种写法避免使用

 

另外,再次将代码改进一下,如下:

class Test(object):


    def __setattr__(self, name, value):

        self.name = value
t = Test()
t.x=1

 

执行结果:

  File "G:/PycharmProject/test/test/test4.py", line 10, in __setattr__
    self.name = value
RuntimeError: maximum recursion depth exceeded

 

 

居然报错了,看报错信息为 “递归错误”,我没用递归啊,怎么会有这个错误呢?

其实,原因很简单:当我们给 t.x 赋值时,调用了 __setattr__()方法,进入该方法;该方法中,又来了一次赋值(self.name = value),又会去调用 __setattr__() 方法,持续这个死循环(子子孙孙无穷尽也,必须要有一代断子绝孙);

我们知道,系统的资源是有限的,丫的你老是申请资源不释放,系统哪来的那么多资源给你自己用?因此,Python解释器规定,递归深度不得超过200(不同版本不一样),你超过了,不好意思,不带你玩了!

所以,我们只好改变上述的问题了:

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


class Test(object):


    def __setattr__(self, name, value):
        print('__setattr__() been called')
        super(Test,self).__setattr__(name, value)
t = Test()
t.x=1 #__setattr__() been called
t.x=2 #__setattr__() been called

 

 

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

  包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)

 

4.1,二次加工标准类型(基于继承实现)

 

 

python3有清空列表的clear方法,python2没有

 

针对python3:

 

#!/usr/bin/env python
#coding:utf8

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) # 检查通过后这里直接用的父类list的append方法添加

    @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)


 

python2:

 

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


# help(list)

class List(list):  # 继承list所有的属性,也可以派生出自己新的,比如append和mid
    def append(self, p_object):
        ' 派生自己的append:加上类型检查'
        if not isinstance(p_object, int):
            raise TypeError('must be int')
        super(List,self).append(p_object) # 检查通过后这里直接用的父类list的append方法添加

    @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)
del l[:]
print(l)

 

 

执行结果:

[1, 2, 3, 4]
[1, 2, 3, 4, 5]
3
[-123, 1, 2, 3, 4, 5]
[]

 

授权与包装的关系:

  授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其它的则保持原样。
  授权的过程,即是所有更新的功能都是由新类的某部分来包装定制处理但已存在的不打算更新的功能就授权给对象的默认属性保留使用

 

 

4.2,练习(clear加权限限制)

 python3:

 

#!/usr/bin/env python
#coding:utf8

class List(list):
    def __init__(self, item, tag=False):
        super().__init__(item)
        self.tag = tag

    def append(self, p_object):
        if not isinstance(p_object, str):
            raise TypeError
        super().append(p_object)

    def clear(self):
        if not self.tag:
            raise PermissionError
        super().clear()


l = List([1, 2, 3], False)
print(l)
print(l.tag)

l.append('saf')
print(l)

# l.clear() #异常

l.tag = True
l.clear()

 

执行结果:

[1, 2, 3]
False
[1, 2, 3, 'saf']

 

4.3,实现授权的关键点就是覆盖__getattr__方法

 

授权示范一:

 

#!/usr/bin/env python
#coding:utf8

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)) # 这里没继承关系,就直接调用生成的句柄本身的内建方法write

    def __getattr__(self, item): # 其他未重设的属性/方法就授权用句柄自带内建方法
        return getattr(self.file,item) # 这里如果item调用的是方法,则返回的是内存地址

f1=FileHandle('b.txt','w+')
f1.write('你好啊')
f1.seek(0)
print(f1.read())
f1.close()

 

授权示范二

 

#!/usr/bin/env python
#coding:utf8


# 我们来加上b模式支持
import time


class FileHandle:
    def __init__(self, filename, mode='r', encoding='utf-8'):
        if 'b' in mode:
            self.file = open(filename, mode)
        else:
            self.file = open(filename, mode, encoding=encoding)
        self.filename = filename
        self.mode = mode
        self.encoding = encoding

    def write(self, line):
        if 'b' in self.mode:
            if not isinstance(line, bytes):
                raise TypeError('must be bytes')
        self.file.write(line)

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

    def __str__(self):
        if 'b' in self.mode:
            res = "<_io.BufferedReader name='%s'>" % self.filename
        else:
            res = "<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" % (self.filename, self.mode, self.encoding)
        return res


f1 = FileHandle('b.txt', 'wb')
# f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气
f1.write('你好啊'.encode('utf-8'))
print(f1)
f1.close()

 

练习题(授权)

 

练习一:

 

#!/usr/bin/env python
#coding:utf8

class List:
    def __init__(self, seq):
        self.seq = seq

    def append(self, p_object):
        ' 派生自己的append加上类型检查,覆盖原有的append'
        if not isinstance(p_object, int):
            raise TypeError('must be int')
        self.seq.append(p_object)

    @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])
print(l)
l.append(4)
print(l)
# l.append('3333333') #报错,必须为int类型

print(l.mid)

# 基于授权,获得insert方法
l.insert(0, -123)
print(l)

 

 

执行结果:

[1, 2, 3]
[1, 2, 3, 4]
3
[-123, 1, 2, 3, 4]

 

练习2:

#!/usr/bin/env python
#coding:utf8

class List:
    def __init__(self, seq, permission=False):
        self.seq = seq
        self.permission = permission

    def clear(self):
        if not self.permission:
            raise PermissionError('not allow the operation')
        self.seq.clear()

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

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


l = List([1, 2, 3])
# l.clear() #此时没有权限,抛出异常


l.permission = True
print(l)
l.clear()
print(l)

# 基于授权,获得insert方法
l.insert(0, -123)
print(l)

 

执行结果:

[1, 2, 3]
[]
[-123]

 

 

 

 

 

 参考:

用来定制类的特殊方法



1、基本定制型
C.__init__(self[, arg1, ...]): 构造器(带一些可选的参数)

C.__new__(self[, arg1, ...]):  构造器(带一些可选的参数);通常用在设置不变数据类型的子类

C.__del__(self) :解构器

C.__str__(self) :可打印的字符输出;内建 str()及 print 语句

C.__repr__(self) :运行时的字符串输出;内建 repr() 和‘ ‘ 操作符

C.__unicode__(self): Unicode 字符串输出;内建 unicode()

C.__call__(self, *args): 表示可调用的实例

C.__nonzero__(self) :为 object 定义 False 值;内建 bool() (从 2.2 版开始)
C.__len__(self) :“长度”(可用于类);内建 len()


2、对象(值)比较 
C.__cmp__(self, obj): 对象比较;内建 cmp()
C.__lt__(self, obj) and C__le__(self,obj) : 小于/小于或等于;对应<及<=操作符
C.__gt__(self, obj) and C.__ge__(self,obj): 大于/大于或等于;对应>及>=操作符
C.__eq__(self, obj) and C.__ne__(self,obj): 等于/不等于;对应==,!=及<>操作符

3、属性
C.__getattr__(self, attr) :获取属性;内建 getattr();仅当属性没有找到时调用
C.__setattr__(self, attr, val) :设置属性
C.__delattr__(self, attr) :删除属性
C.__getattribute__(self, attr) :获取属性;内建 getattr();总是被调用
C.__get__(self, attr) : (描述符)获取属性
C.__set__(self, attr, val) : (描述符)设置属性
C.__delete__(self, attr) : (描述符)删除属性


4、定制类/模拟类型
数值类型:二进制操作符
C.__*add__(self, obj): 加;+操作符
C.__*sub__(self, obj) :减;-操作符
C.__*mul__(self, obj) :乘;*操作符
C.__*div__(self, obj) :除;/操作符
C.__*truediv__(self, obj) : True 除;/操作符
C.__*floordiv__(self, obj) :Floor 除;//操作符
C.__*mod__(self, obj) :取模/取余;%操作符
C.__*divmod__(self, obj) :除和取模;内建 divmod()
C.__*pow__(self, obj[, mod]) :乘幂;内建 pow();**操作符
C.__*lshift__(self, obj): 左移位;<<操作符



5、定制类/模拟类型
数值类型:二进制操作符
C.__*rshift__(self, obj): 右移;>>操作符
C.__*and__(self, obj): 按位与;&操作符
C.__*or__(self, obj) :按位或;|操作符
C.__*xor__(self, obj) :按位与或;^操作符


6、数值类型:一元操作符
C.__neg__(self) :一元负
C.__pos__(self) :一元正
C.__abs__(self) :绝对值;内建 abs()
C.__invert__(self): 按位求反;~操作符

7、数值类型:数值转换
C.__complex__(self, com): 转为 complex(复数);内建 complex()
C.__int__(self) :转为 int;内建 int()
C.__long__(self) :转为 long;内建 long()
C.__float__(self): 转为 float;内建 float()

8、数值类型:基本表示法(String)
C.__oct__(self): 八进制表示;内建 oct()
C.__hex__(self) :十六进制表示;内建 hex()

9、数值类型:数值压缩
C.__coerce__(self, num) 压缩成同样的数值类型;内建 coerce()
C.__index__(self)g 在有必要时,压缩可选的数值类型为整型(比如:用于切片索引等等

10、序列类型
C.__len__(self): 序列中项的数目
C.__getitem__(self, ind): 得到单个序列元素
C.__setitem__(self, ind,val) :设置单个序列元素
C.__delitem__(self, ind) :删除单个序列元素

11、序列类型
C.__getslice__(self, ind1,ind2): 得到序列片断
C.__setslice__(self, i1, i2,val) :设置序列片断
C.__delslice__(self, ind1,ind2) :删除序列片断
C.__contains__(self, val) : 测试序列成员;内建 in 关键字
C.__*add__(self,obj) :串连;+操作符
C.__*mul__(self,obj) :重复;*操作符
C.__iter__(self) :创建迭代类;内建 iter()

12、映射类型
C.__len__(self): mapping 中的项的数目
C.__hash__(self) :散列(hash)函数值
C.__getitem__(self,key): 得到给定键(key)的值
C.__setitem__(self,key,val): 设置给定键(key)的值
C.__delitem__(self,key) :删除给定键(key)的值
C.__missing__(self,key) :给定键如果不存在字典中,则提供一个默认值

 

 

 

参考:https://www.cnblogs.com/oceanicstar/p/8850111.html

https://www.cnblogs.com/shangpolu/p/6219765.html

https://www.cnblogs.com/Jimmy1988/p/6804095.html

super用法参考:

http://www.jb51.net/article/87324.htm

http://www.runoob.com/python/python-func-super.html

 

posted @ 2019-06-18 23:57  钟桂耀  阅读(271)  评论(0编辑  收藏  举报