Python基础-week06 面向对象编程进阶
Posted Jame-mei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python基础-week06 面向对象编程进阶相关的知识,希望对你有一定的参考价值。
一.反射
1.定义:指的是通过字符串来操作类或者对象的属性
2.为什么用反射?
减少冗余代码,提升代码质量。
3.如何用反射?
class People: country=\'China\' def __init__(self,name): self.name=name obj=People(\'jame\') #hasattr #print(\'country\' in People.__dict__) print(hasattr(People,\'country\')) #getattr #print(People.__dict__[\'country\']) #print(getattr(People,\'country)) #如果取不到值,会报错。 print(getattr(People,\'country\',None)) #None如果取不到值不报错,返回None #setattr #obj.age=18 #print(obj.__dict__) setattr(obj,\'age\',18) print(obj.__dict__) #{\'name\': \'jame\', \'age\': 18} setattr(People,\'x\',111) print(People.__dict__) #{......, \'__doc__\': None, \'x\': 111} #delattr delattr(People,\'x\') print(People.__dict__) #{......, \'__doc__\': None}
class Ftp: def get(self): print(\'get...\') def put(self): print(\'put...\') def auth(self): print(\'auth...\') def run(self): while True: cmd=input(\'Please input:\').strip() #cmd=\'get if hasattr(self,cmd): method=getattr(self,cmd) method() else: print(\'You please input error\') obj=Ftp() obj.run()
动态导入模块<了解>:
import importlib __import__(\'import_lib.metaclass\') #这是解释器自己内部用的 #importlib.import_module(\'import_lib.metaclass\') #与上面这句效果一样,官方建议用这个
二.一些内置方法 和 内置函数介绍
1.isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)检查是否obj是否是类 cls 的对象。
issubclass(sub,super) 检查sub类是否是 super类的派生类。
class Foo(object): pass class bar(Foo): pass obj=Foo() #1 isinstance判断对象是否属于某类 res1=isinstance(obj,Foo) print(res1) #True #2 issubclass 判断bar类是否是 Foo 类的派生类 res2=issubclass(bar,Foo) print(res2) #true
2.__setattr__ ,__delattr__ ,__getattr__
配合反射机制使用,效果还不错.
class People: country=\'China\' def __init__(self,name): self.name=name obj=People(\'jame\') #hasattr #print(\'country\' in People.__dict__) print(hasattr(People,\'country\')) #getattr #print(People.__dict__[\'country\']) #print(getattr(People,\'country123\')) #如果取不到值,会报错。AttributeError: type object \'People\' has no attribute \'country123\' print(getattr(People,\'country123\',None)) #None如果取不到值不报错,返回None #setattr #obj.age=18 #print(obj.__dict__) setattr(obj,\'age\',18) print(obj.__dict__) #{\'name\': \'jame\', \'age\': 18} setattr(People,\'x\',111) print(People.__dict__) #{......, \'__doc__\': None, \'x\': 111} #delattr delattr(People,\'x\') print(People.__dict__) #{......, \'__doc__\': None}
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\') def __delattr__(self, item): print(\'from delattr\') self.__dict__.pop(item) #1 __setattr__ 添加、修改属性会触发它的执行 f1=Foo(10) #from setattr print(f1.__dict__) #{} #因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.__dict__[\'a\']=3 f1.__dict__[\'b\']=4 print(f1.__dict__) #{\'a\': 3, \'b\': 4} #2 __deattr__ 删除属性的时候会触发 #f1.__dict__[\'a\']=10 #我们可以通过修改属性字典,来完成添加、修改属性的操作 del f1.a #from delattr print(f1.__dict__) #{\'b\':4}还剩b # 3 __getattr__ 只有使用点调用属性且属性不存在的时候才会触发 f1.abc #from getattr:你找的属性不存在.
class Ftp: def get(self): print(\'get...\') def put(self): print(\'put...\') def auth(self): print(\'auth...\') def run(self): while True: cmd=input(\'Please input:\').strip() #cmd=\'get if hasattr(self,cmd): method=getattr(self,cmd) method() else: print(\'You please input error\') obj=Ftp() obj.run()
3.__getattribute__
# @Time : 2018/8/20 17:19 # @Author : Jame # class Foo: # def __init__(self,x): # self.x=x # # def __getattr__(self, item): # print(\'执行的是__getattr__\') # # # # # f1=Foo(100) # print(f1.x) # f1.xxx #若访问的不存在,则 “执行的是__getattr__” class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print(\'不管是否存在都执行的是__getattribute__\') f1=Foo(200) f1.x f1.xxx
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print(\'如果不存在则执行__getattr__\') def __getattribute__(self, item): print(\'不管是否存在都执行的是__getattribute__\') raise AttributeError(\'哈哈 嘿嘿 哟哟\') f1=Foo(200) f1.x f1.xxx
#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
4.描述符(__get__ , __set__, __delete__)
(1).1 描述符是什么?
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
#1 定义一个描述符 class Foo: def __get__(self, instance, owner): print(\'__get__\') def __set__(self, instance, value): print(\'__set__\') def __delete__(self, instance): print(\'__delete__\')
(2).描述符是干什么的,何时触发描述符中的3个方法呢?
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中__init__())
包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法!例如:
#2 描述符的使用 class Foo2: def __get__(self, instance, owner): print(\'触发 __get__\') def __set__(self, instance, value): print(\'触发 __set__\') def __delete__(self, instance): print(\'触发 __delete__\') #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法! f2=Foo2() f2.name=\'jame\' print(f2.name) del f2.name
#何地?:定义成另外一个类的类属性
#何时?:且看下列演示
class Str: def __get__(self, instance, owner): print(\'Str 调用\') def __set__(self, instance, value): print(\'str 设置\') def __delete__(self, instance): print(\'str 删除\') 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(\'jame\',18) #触发Str 设置,Int 设置! #1 描述符str的使用 调用,设置,删除 #p1.name #p1.name=\'tom\' #del p1.name \'\'\' Str 调用 str 设置 str 删除 \'\'\' #2 描述符int的使用 调用,设置,删除 #p1.age #p1.age=30 #del p1.age \'\'\' Int 调用 Int 设置 Int 删除 \'\'\' #3 我们来瞅瞅到底发生了什么? print(p1.__dict__) print(People.__dict__) #补充 print(type(p1) == People) #True,type(p1) 查看p1是哪个类实例化来的。 print(type(p1).__dict__==People.__dict__) #True
(3).描述符分两种
1).数据描述符:至少实现了__get__()和__set__()
1 class Foo: 2 def __set__(self, instance, value): 3 print(\'set\') 4 def __get__(self, instance, owner): 5 print(\'get\')
2).非数据描述符:没有实现__set__()
class Foo: 2 def __get__(self, instance, owner): 3 print(\'get\')
(4).注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()
#描述符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): self.name=name self.age=age People.name #调用类属性name,本质就是在调用描述符Str,触发了__get__() People.name=\'egon\' #赋值并没有触发__set__()的设置 del People.name #删除也并没有触发 __del__() 的设置 #结论:描述符对类没有作用-------->傻逼到家的结论 \'\'\' 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级 People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() People.name=\'egon\' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__() del People.name #同上 \'\'\'
#描述符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) #str设置,触发! #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性 p1.name=\'egonnnnnn\' #str设置,触发! p1.name #str调用,触发! print(p1.__dict__) #实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了 del p1.name #str删除,触发! ###数据描述符>实例属性
class Foo: def func(self): print(\'我胡汉三又回来了\') f1=Foo() f1.func() #调用类的方法,也可以说是调用非数据描述符 #函数是一个非数据描述符对象(一切皆对象么) print(dir(Foo.func)) print(hasattr(Foo.func,\'__set__\')) #False print(hasattr(Foo.func,\'__get__\')) #True print(hasattr(Foo.func,\'__del__\')) #False #也有人可能问,描述符不都是类吗,函数怎么算也应该是一个对象,怎么就是描述符了。 #描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么 #函数就是有一个非描述符类实例化得到的一个对象 #没错,字符串也是一样 f1.func=\'这是实例属性啊\' print(f1.func) del f1.func #删除了非数据 f1.func() #我胡汉三又回来了
class Foo: def __set__(self, instance, value): print(\'foo set\') def __get__(self, instance, owner): print(\'foo 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) #触发foo set # # r1.name #触发foo get # # r1.name=\'厨房\' #触发 foo set class Foo1: def __get__(self, instance, owner): print(\'Foo1 get\') class Room1: name=Foo1() def __init__(self,name,width,length): self.name=name self.width=width self.length=length #name 是一个非数据描述符,因为name=Foo1() 但是Foo1中没有实现set方法,因而比实例属性的优先级更低 #对实例的属性操作,触发的都是实例自己 r2=Room1(\'大厦\',100,100) r2.name r2.name=\'高塔\'
# @Time : 2018/8/24 14:56 # @Author : Jame class Foo: def func(self): print(\'我胡汉三回来了\') def __getattr__(self, item): print(\'找不到了当然是来找我啦\',item) f1=Foo() f1.xxxx #找不到了当然是来找我啦 xxxx
(5).描述符的使用
众所周知,python是弱类型语言,即参数的复制没有任何类型限制,下面我们通过描述符机制来实现类型的功能。
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 以上是关于Python基础-week06 面向对象编程进阶的主要内容,如果未能解决你的问题,请参考以下文章