Python之路(第二十五篇) 面向对象初级:反射、内置方法

时间:2023-03-08 15:51:38

[TOC]

一、反射

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

反射的四个函数

1、hasattr(object,name)

判断对象/类/模块中是否有某种属性或者方法,注意第二个参数是字符串

该函数返回的是,存在返回True,不存在返回False。

2、getattr(object, name, default=None)

获取对象/类/模块中某种属性或者方法的地址,第三个参数是如果没有获取到则返回自己设置的默认值,注意不能使用关键字参数的形式,注意第二个参数是字符串

该函数返回的是属性的值或者方法的内存地址

3、setattr(x, y, v)

设置对象/类/模块中某种属性或者方法的地址,注意第二个参数是字符串

,第三个参数是要设置的属性的的值或者方法的形式

该函数没有返回值

4、delattr(x, y)

删除对象/类/模块中某种属性或者方法的地址,注意第二个参数是字符串

使用方法例子

  class Goods:
country = "china"

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

def rise(self):
self.price = self.price + 1

def discount(self):
self.price = self.price - 2

@classmethod
def func(cls):
print("类方法")

@staticmethod
def func2():
print("静态方法")

g = Goods("apple",5)

#通过反射检测是否存在某个属性或者方法,存在返回True,不存在返回False
print(hasattr(g,"name"))
print(hasattr(g,"country")) #对象没有向上寻找父类的属性
print(hasattr(g,"rise"))
print(hasattr(g,"xxx")) #检测不到属性或者方法的存在返回False
print(hasattr(g,"func")) #对象也可以检测类方法
print(hasattr(Goods,"rise")) #也可以直接检测类的属性和方法
print(hasattr(Goods,"country"))
print(hasattr(Goods,"func2")) #类也可以检测,类也可以作为一个对象


#获取类\对象\模块的属性或者方法内存地址
print(getattr(g,"name"))
print(getattr(g,"country")) #获取属性返回的是属性的值
print(getattr(g,"rise")) #获取方法返回的是方法的内存地址
# print(getattr(g,"xxx")) #获取不存在的属性或者方法,如果没有第三个参数会报错,有第三个参数则返回设置的第三个参数
print(getattr(g,"xxx",None))
print(getattr(Goods,"func"))
print(getattr(Goods,"rise"))
print(getattr(Goods,"country"))
print(getattr(Goods,"func"))
print(getattr(Goods,"func2"))


#设置对象/类/模块的属性或者方法
print(g.__dict__)
# print(Goods.__dict__)
setattr(g,"name","banana")
setattr(g,"country","CHINA") #注意这里并非修改的是类的属性,而是在对象中增加了country属性
setattr(g,"rise",lambda self:self.price + 10) #设置已经存在的方法需要自己再次传值
setattr(g,"xxx",lambda self:self.price + 5) #
setattr(Goods,"country","CA")
print(g.__dict__)
print(g.rise(g)) #输出结果15 ,这里修改的是根据修改后的方法得出的值,而非原有的值
print(g.xxx(g)) #输出结果10
print(g.price) #输出结果5,原有的值在setattr后不变


# 删除对象/类中属性或者方法
print(g.__dict__)
print(Goods.__dict__)
delattr(g,"name")
delattr(Goods,"discount") #对象只有数据属性,但是可以访问类的函数属性,这里删除的是类的函数属性
delattr(g,"aaa") #如果删除的不存在则报错
print(g.__dict__)
print(Goods.__dict__)

  

分析:类也是对象,也可以作为第一个参数,第二个参数一定是字符串。

导入当前模块成员的反射

  
  # 导入当前模块的成员的反射

import sys

val = "v1"

def func1():
print("s1")

def func2():
print("s2")


this_modules = sys.modules[__name__] #sys.modules查看所有被导入的模块,以字典的形式存储,
# __name__是当前模块的名字,如果当前文件是执行文件则__name__ = "__mian__",如果不是则是文件名

print(hasattr(this_modules,"val")) #这里本文件是一个模块,也可以作为一个对象进行反射
print(hasattr(this_modules,"func1"))
print(getattr(this_modules,"val"))
print(getattr(this_modules,"func1"))
print(getattr(this_modules,"func2"))

  

输出结果

  True
True
v1
<function func1 at 0x021B5DB0>
<function func2 at 0x021B5D68>

  

导入其他文件模块的反射

例子

被导入文件模块

文件名:my_module.py

文件内容

  val  = "hello"

def func1():
print("from func1")

class A:
c_val = "world"

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

def func2(self):
print("from class A func2")

  

执行文件index.py,与被导入文件模块放在同一文件夹下

  
  import my_module

print(hasattr(my_module,"val"))
print(hasattr(my_module,"func1"))
print(hasattr(my_module.A,"c_val"))
print(hasattr(my_module.A,"func2"))


a = my_module.A("nick",18)

print(getattr(my_module,"val"))
print(getattr(my_module,"func1"))
print(getattr(my_module.A,"c_val")) #导入模块中的类的反射
print(getattr(my_module.A,"func2"))
print(getattr(a,"name")) #导入模块中类的对象的反射
print(getattr(a,"c_val"))
print(getattr(a,"func2"))

res = getattr(my_module,"A") #反射一个类
print(res)
res() # 这里就是实例化一个类,产生了一个对象

  

分析:导入其他模块文件也可以用反射,也可以反射一个类

导入内置模块也可以用反射

例子


 import time

print(hasattr(time,"asctime"))
print(hasattr(time,"localtime"))

res = getattr(time,"asctime")
print(res())

res2 = getattr(time,"strftime") #获取方法函数内存地址
print(res2("%Y--%m--%d %H:%M:%S")) #有参数可以在调用执行时时传入参数

  

输出结果

  True
True
Thu Jul 19 11:48:19 2018
2018--07--19 11:48:19

  

反射应用小例子

  
  class Ftpserver:

def __init__(self):
pass

def get(self):
print("下载中。。。。。")

def put(self):
print("上传中。。。。。")

def run(self):

cmds = input("请输入命令:")
cmd = cmds.split()[0]
if hasattr(self,cmd):
func = getattr(self,cmd)
func()

f = Ftpserver()
f.run()

  

二、内置函数

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

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

  
  class Foo:
pass

obj = Foo()
print(isinstance(obj,Foo)) #输出结果 True

  

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

  
  class Foo(object):
pass

class Son(Foo):
pass

print(issubclass(Son,Foo)) #输出结果True
print(issubclass(Foo,object)) #输出结果True

  

2、__setattr__,__delattr__,__getattr__

__setattr___添加/修改属性会触发它的执行

__delattr___删除属性的时候会触发

__getattr__只有在使用点调用属性且属性不存在的时候才会触发,调用存在的属性或者方法时不触发

例子1  __setattr__


 class Foo():

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

def func(self):
print("func------")

def __setattr__(self, key, value):
print("执行__setattr__")
# self.key=value #进入无限递归
# self.__dict__[key]=value #应该使用它

p = Foo("jack") #实例化的过程中会增加属性,所以执行__setattr__ :执行__setattr__
print(p.__dict__) #而__setattr__目前只是执行了打印工作,所以这里并没有为name赋值,也就是没有name属性
# 去掉 # self.__dict__[key]=value语句的注释即可为name属性赋值

  

输出结果

  
  执行__setattr__
{}

  

例子2 __getattr__

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

def func(self):
print("func------")

def __getattr__(self, item):
print("执行__getattr__",item)

p = Foo("jack") #实例化的过程中会增加属性,
print(p.__dict__)
print(p.name)
print(p.age) #调用不存在的属性会执行__getattr__

  

输出结果

  
  {'name': 'jack'}
jack
执行__getattr__ age
None

  

例子3 __setattr__

  class Foo():

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

def func(self):
print("func------")


def __delattr__(self, item):
print("执行__delattr__")
# del self.item #无限递归
self.__dict__.pop(item)

def __setattr__(self, key, value):
print("执行__setattr__")
# self.key=value #进入无限递归
self.__dict__[key]=value #应该使用它

p = Foo("jack") #实例化的过程中会增加属性,执行__setattr__,执行self.__dict__[key]=value语句,为name属性赋值
print(p.name) #
p.age = 18 #执行__setattr__方法,对象新增加属性
print(p.__dict__) #输出结果{'name': 'jack', 'age': 18}
del p.age #调用__delattr__方法,删除对象的age属性
print(p.__dict__) #输出结果{'name': 'jack'},此时已经不包含age属性

  

输出结果

  
  执行__setattr__
jack
执行__setattr__
{'name': 'jack', 'age': 18}
执行__delattr__
{'name': 'jack'}

  

3、二次加工标准类型

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

例子


 class LIST(list):  #继承list所有的属性

def append(self, object):
if type(object) is not str: #这里对append方法进行二次处理,增加了新的功能
print("%s 必须是字符串"%object)
super().append(object)
#list.append(self,object) 与上面一句作用相同,都是调用父类list的append方法

@property #使用property使得mid方法更像对象的数据属性,可以直接获得返回值
def mid(self): #获得列表中间的元素的值
index = len(self)
mid_index = index//2 #这里是取除以2的整数商部分,向下取整
return self[mid_index]


la = LIST("hellonick") #调用list的方法将字符串转为列表
print(la,type(la)) #输出结果 ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class '__main__.LIST'>
# 这里的la是类LIST的一个对象
print(la.mid) #输出结果 o

lb = list("hellonick")
print(lb,type(lb)) #输出结果['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class 'list'>,
# 这里直接显示lb是列表类型,也是list的一个对象

  

输出结果

  
  ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class '__main__.LIST'>
o
['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k'] <class 'list'>
111 必须是字符串
['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k', 111, 'aada']

  

例子2

  
  class LIST(list):  #继承list所有的属性

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

def append(self, object):
if type(object) is not str: #这里对append方法进行二次处理,增加了新的功能
print("%s 必须是字符串"%object)
super().append(object)
#list.append(self,object) 与上面一句作用相同,都是调用父类list的append方法

def clear(self):
if not self.tag: #改写原列表方法clear,增加权限限制
raise PermissionError
super().clear()

la = LIST("hellonick") #调用list的方法将字符串转为列表,输出结果 ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k']
print(la)
print(la.tag)
# la.clear() #会报错


lb = LIST([2,3,4,5,6,7])
print(lb)
# lb.clear() #会报错
print(lb.tag)

lc = LIST([5,3,4,5,7],)
lc.tag = True
lc.clear()
print(lc) #输出结果为 []

  

输出结果

  
  ['h', 'e', 'l', 'l', 'o', 'n', 'i', 'c', 'k']
False
[2, 3, 4, 5, 6, 7]
False
[]

  

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

简单来说就是更新的功能用自己定义类的方法,没有更新的功能用父类的方法。自己定义了一个方法,与父类同名,那么就用自己的定义的方法,其他的方法继续用父类的方法。授权也是一种包装,但授权的实现不是通过继承,而是使用__getattr__方法。

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

例子

  
  class FileHandle:

def __init__(self,file,mode,encoding):
self.file = open(file,mode,encoding=encoding)

def __getattr__(self, item):
return getattr(self.file,item)
#这里调用属性的时候自动执行__getattr__方法,执行之后用getattr方法返回原属于open的方法

def read(self):
print("读取文件中。。。")
#如果有同名的方法那么就用自己定义的方法,不用__getattr__获得的方法,
# 因为__getattr__只有在使用点调用属性且属性不存在的时候才会触发 ​
f = FileHandle("test.txt","w+","utf-8")
f.write("aaaaa")
f.seek(0)
data = f.read() #这里是执行了FileHandle的自己定义的read()方法,所以输出结果是None
print(data)

  

输出结果

  
  读取文件中。。。
None

  

例子2

  
  import time

class FileHandle:

def __init__(self,file,mode,encoding):
self.file = open(file,mode,encoding=encoding)

def __getattr__(self, item):
return getattr(self.file,item)
#这里调用属性的时候自动执行__getattr__方法,执行之后用getattr方法返回原属于open的方法

def write(self,msg):
self.file.write("%s %s"%(time.strftime("%Y-%m-%d %X"),msg)) #调用self.file中open的方法
#为每次写入增加时间

f = FileHandle("test.txt","w+","utf-8")
f.write("aaaaa")
f.seek(0)
data = f.read()
print(data)

  

输出结果


 2018-08-03 16:12:52 aaaaa