面向对象进阶

isinstance(obj,cls) 检查obj是否是cls的实例

issubclass(obj,cls) 检查obj是否是cls的继承类

反射

通过字符串的形式操作对象的相关的属性。

主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)

hasattr(obj,name) 检查obj下是否有叫name属性,name必须字符串

getattr(obj,name,default=None)  查询obj下的属性,有返回name对应的参数,没有返回default

setattr(obj,x,y)  给obj设置属性,x必须字符串

delattr(obj,x)    删除obj下的属性,x必须字符串

示例:

class BlackMedium:
    feature='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_house(self):
        print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name)
    def rent_house(self):
        print('%s 黑中介租房子啦,傻逼才租呢' %self.name)

b1=BlackMedium('万成置地','回龙观天露园')

#检测是否含有某属性
print(hasattr(b1,'name'))
print(hasattr(b1,'sell_house'))

#获取属性
n=getattr(b1,'name')
print(n)
func=getattr(b1,'rent_house')
func()

# getattr(b1,'aaaaaaaa') #报错
print(getattr(b1,'aaaaaaaa','不存在啊'))

#设置属性
setattr(b1,'sb',True)
setattr(b1,'show_name',lambda self:self.name+'sb')
print(b1.__dict__)
print(b1.show_name(b1))

#删除属性
delattr(b1,'addr')
delattr(b1,'show_name')
delattr(b1,'show_name111')#不存在,则报错

print(b1.__dict__)

反射的好处

好处一:实现可插拔机制

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

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

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

在模块中导入自己:

	this_modul=sys.modules[__name__]


使用字符串导入模块:

	1、__import__("time")

	2、import importlib
	
		importlib.import_module("time")

	推荐使用第二种

setattr,getattr,delattr 操作属性,在类中的用法和意义

setattr

设置属性的时候会触发。实质是操作类.__dict__

delattr

删除类的属性时会触发,实质是操作类.__dict__

getattr

调用类中不存在的属性时会触发。

三者的用法演示:

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

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


    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #这就无限递归了,你好好想想
        # self.__dict__[key]=value #应该使用它

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        self.__dict__.pop(item)

#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)

#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)

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

setitem,getitem,__delitem__的使用方法

作用同上。只不过是调用的时候__setattr__那中方法是用 . 去调用。而这种是用["值"]的方式去调用。

class Foo:
    x = 1

    def __init__(self, y):
        self.y = y

    def __getitem__(self, item):   #执行对象[属性],找不到属性的时候会执行这个代码。
        print('----> from getattr:你找的属性不存在')

    def __setitem__(self, key, value):  #执行对象[属性]=值,的时候会执行这个代码
        self.__dict__[key]=value
 
    def __delitem__(self, item):        #执行 del 对象[属性] 的时候会执行这个代码。
        self.__dict__.pop(item)

f1 = Foo(10)   #初始化触发init
print(f1.__dict__)

f1["z"]=3      #赋值触发setitem
print(f1.__dict__)

f1["a"]        #查找不存在的属性,触发getitem

f1.__dict__['a'] = 3
del f1["a"]      #删除触发delitem
print(f1.__dict__)

定制自己的数据类型:二次加工标准类型

基于类继承实现

class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid
    def append(self, p_object):
        
        if not isinstance(p_object,int):  #' 派生自己的append:加上类型检查'
            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)

授权的方式

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


#重写list
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)

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

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

自定制格式化字符串__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):
        if not format_spec or format_spec not in date_dic: 如果输入的为空或者输入的格式不在规定的字典中,赋予默认格式

            format_spec='ymd'

        fmt=date_dic[format_spec]
        return fmt.format(self)


d1=Date(2016,12,29)
print(format(d1))           #默认输出模式
print('{:mdy1}'.format(d1)) #没有mdy1这个模式。所以默认ymd模式输出


print('{:mdy}'.format(d1))  #mdy输出模式


print(format(d1,"dmy"))     #dmy输出模式
print("{:dmy}".format(d1))  #dmy输出模式,同上

__next__和__iter__实现迭代器协议

from collections import Iterable(可迭代对象),Iterator(迭代器)

class Foo:
    def __init__(self,start=0,stop=0):
        self.start=start
        self.stop=stop
        self.con=0


    def __iter__(self):
        return self


    def __next__(self):
        if self.con >= self.start and self.start >=self.stop :
            raise StopIteration
        if self.stop==0:
            if self.con < self.start:
                n = self.con
                self.con += 1
                return n
        else:
            self.con=self.stop
            n=self.start
            self.start+=1
            return n

f=Foo(1,6)
print(isinstance(f,Iterator))	 #判断f是否是迭代器

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

for i in Foo(6):
    print(i)

doc

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

print(Foo.__doc__)

输出:'我是描述信息'

这个属性无法被继承

enter__和__exit

class Open:
    def __init__(self,name,mode="w"):
        self.x=open(name,mode=mode)
        self.name=name
        self.mode=mode

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

    def __exit__(self, exc_type, exc_val, exc_tb):  #with中代码块执行完毕后执行
        print("exc_type",exc_type)                  #捕获异常。获取异常的各个信息。
        print("exc_val",exc_val)
        print("exc_tb",exc_tb)
        return True



    def write(self,value):
        self.x.write(value)

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

# a=Open("a.txt","r")
# print(a.read())
# a.seek(0)
# print(a.read())

with Open("a.txt","r") as f:
    print(f.read())
    raise TypeError("error!")

print("-------------------------")

call

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

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

class Foo:

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

        print('__call__')


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

元类:

什么是元类?

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

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

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

创建类的两种方法

方式一:

1 class Foo:
2     def func(self):
3         print('from func')

方式二:

1 def func(self):
2         print('from func')
3 x=1
4 Foo=type('Foo',(object,),{'func':func,'x':1})

一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

自定制元类

class B(type):
    def __init__(self,name,bases=None,dict=None):
        print("init B")
        self.name=name

    def __call__(self, *args, **kwargs):
        print("call")
        obj=self.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj

class A(object,metaclass=B):
    def __init__(self,name):
        print("init A")
        self.name=name

    def __new__(cls, *args, **kwargs):
        print("new A")
        return super().__new__(cls)

a=A("name1")
print(a.__dict__)

精简版

class Mytype(type):

    def __init__(self,what,bases=None,dict=None):
        print(what,bases,dict)

    def __call__(self, *args, **kwargs):
        print('--->')
        obj=object.__new__(self)
        self.__init__(obj,*args,**kwargs)
        return obj


class Room(metaclass=Mytype):

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

r1=Room('alex')
print(r1.__dict__)

总结

元类总结

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__方法
'''
posted @ 2017-04-24 16:42  高跃  阅读(111)  评论(0编辑  收藏  举报