[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

  

Python之路(第二十五篇) 面向对象初级:反射、内置方法的更多相关文章

  1. Python之路(第二十九篇) 面向对象进阶:内置方法补充、异常处理

    一.__new__方法 __init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __ ...

  2. Python之路(第二十六篇) 面向对象进阶:内置方法

    一.__getattribute__ object.__getattribute__(self, name) 无条件被调用,通过实例访问属性.如果class中定义了__getattr__(),则__g ...

  3. Python之路(第二十四篇) 面向对象初级:多态、封装

    一.多态 多态 多态:一类事物有多种形态,同一种事物的多种形态,动物分为鸡类,猪类.狗类 例子 import abc class H2o(metaclass=abc.ABCMeta): ​ def _ ...

  4. Python之路(第二十二篇) 面向对象初级:概念、类属性

    一.面向对象概念 1. "面向对象(OOP)"是什么? 简单点说,“面向对象”是一种编程范式,而编程范式是按照不同的编程特点总结出来的编程方式.俗话说,条条大路通罗马,也就说我们使 ...

  5. Python之路(第二十八篇) 面向对象进阶:类的装饰器、元类

    一.类的装饰器 类作为一个对象,也可以被装饰. 例子 def wrap(obj): print("装饰器-----") obj.x = 1 obj.y = 3 obj.z = 5 ...

  6. Python之路(第十五篇)sys模块、json模块、pickle模块、shelve模块

    一.sys模块 1.sys.argv 命令行参数List,第一个元素是程序本身路径 2.sys.exit(n) 退出程序,正常退出时exit(0) 3.sys.version . sys.maxint ...

  7. Python之路(第二十七篇) 面向对象进阶:内置方法、描述符

    一.__call__ 对象后面加括号,触发执行类下面的__call__方法. 创建对象时,对象 = 类名() :而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()( ...

  8. Python之路【第五篇】:面向对象及相关

    Python之路[第五篇]:面向对象及相关   面向对象基础 基础内容介绍详见一下两篇博文: 面向对象初级篇 面向对象进阶篇 其他相关 一.isinstance(obj, cls) 检查是否obj是否 ...

  9. Python之路【第五篇】:面向对象和相关

    Python之路[第五篇]:面向对象及相关   面向对象基础 基础内容介绍详见一下两篇博文: 面向对象初级篇 面向对象进阶篇 其他相关 一.isinstance(obj, cls) 检查是否obj是否 ...

随机推荐

  1. 1.5.6、CDH 搭建Hadoop在安装之前(定制安装解决方案---使用Cloudera Manager模板创建CDH群集)

    使用Cloudera Manager模板创建CDH群集 您可以通过从Cloudera Manager管理的现有CDH群集导出群集模板来创建新的CDH群集.然后,您可以修改模板并使用它在新的主机集上创建 ...

  2. 物料没加DUMMY

    会加入DUMMY的表 IN_ITEM,IN_ITEM_SITE,IN_SALES_ORDER 加入DUMMY的存储过程名为SAP_MATERIAL_SO. FP_CHANGE_MO_ROUTING的第 ...

  3. love is ... ...

    16 years old, love is dream.20 years old, love is sex.30 years old, love is marriage. 40 years old, ...

  4. Ajax图片异步上传并回显

    1.jsp页面 <td width="20%" class="pn-flabel pn-flabel-h"></td> <td w ...

  5. 【Spider】使用命令行启动时,能正常抓取,但是在pycharm直接运行不能保存数据

    通过cmd 运行命令scrapy crawl [爬虫名称]可以正常运行,并把数据保存到json文件和下载爬取的图片 但是在项目的spiders目录下的 firstTestSpider.py文件里添加: ...

  6. hibernate 中,出现了错误 "node to traverse cannot be null!" 如何改正

    这个错误基本上是因为hql语句写错了而造成的, 返回找hql输出一下发现, hql语句中间少了几个空格, 写成了String hql = "from"+className+&quo ...

  7. Python设计模式 - 总览(更新中...)

    最近打算重构部分python项目,有道是"工欲善其事,必先利其器",所以有必要梳理一下相关设计模式.每次回顾基本概念或底层实现时都会有一些新的收获,希望这次也不例外. 本系列打算先 ...

  8. leetcode 栈和队列类型题

    1,Valid Parentheses bool isVaild1(string& s) { // 直接列举,不易扩展 stack<char> stk; ; i < s.le ...

  9. 让Ubuntu可以压缩/解压缩RAR文件

    ubuntu刚安装的时候是不能解压rar文件的,只有在安装了解压工具之后,才可以解压. 安装:sudo apt-get install unrar卸载:sudo apt-get remove unra ...

  10. javaweb导出excel

    百度找了半天也没找到一个提供有效思路的,全都告诉我此路不通 html表格数据粘贴到txt,然后改后缀为xsl,打开,发现二者无缝对接 @参考文章.@参考前任项目 /** * @todo * @para ...