反射
在Python中,反射指的是通过字符串来操作对象的属性,涉及到四个内置函数的使用(Python中一切皆对象,类和对象都可以用下述四个方法)
-
hasattr(对象名, 属性名) : 判断某个对象是否存在某个属性
-
getattr(对象名, 属性名, 默认值) : 从对象中取出属性,第三个值是默认值,当属性不存在时返回默认值
-
setattr(对象名, 属性名, 属性值) : 为对象增加新的属性和值
-
delattr(对象名, 属性名) : 删除对象的属性
class Teacher:
def __init__(self,full_name):
self.full_name =full_name
t=Teacher('Egon Lin')
# hasattr(object,'name')
hasattr(t,'full_name') # 按字符串'full_name'判断有无属性t.full_name
# getattr(object, 'name', default=None)
getattr(t,'full_name',None) # 等同于t.full_name,不存在该属性则返回默认值None
# setattr(x, 'y', v)
setattr(t,'age',18) # 等同于t.age=18
# delattr(x, 'y')
delattr(t,'age') # 等同于del t.age
基于反射可以十分灵活地操作对象的属性,比如将用户交互的结果反射到具体的功能执行
>>> class FtpServer:
... def serve_forever(self):
... while True:
... inp=input('input your cmd>>: ').strip()
... cmd,file=inp.split()
... if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
... func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
... func(file)
... def get(self,file):
... print('Downloading %s...' %file)
... def put(self,file):
... print('Uploading %s...' %file)
...
>>> server=FtpServer()
>>> server.serve_forever()
input your cmd>>: get a.txt
Downloading a.txt...
input your cmd>>: put a.txt
Uploading a.txt...
补充: python中一切皆对象,模块也是对象,也可通过反射获取模块中的属性方法
反射的好处
实现可插拔机制
有俩程序员,一个lili,一个是eg,lili在写程序的时候需要用到eg所写的类,但是eg去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等eg度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
# eg没有完成的代码
class FtpClient():
# ftp客户端,但是还没实现功能
def __init__(self, addr):
self.addr = addr
# lili的代码
#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
func_get=getattr(f1,'get')
func_get()
else:
print('---->不存在此方法')
print('处理其他的逻辑')
不影响lili的代码编写
内置方法
Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发.
-
__init__(self)
1.对象绑定方法
2.实例化对象时触发
场景:实例化对象时,会将 空对象和参数传给__init__
-
__str__(self)
1.对象绑定方法
2.打印obj时触发
场景:可以自定义print(obj)打印结果
注意: 必须要有返回值,且是字符串类型
-
__del__(self)
1.对象绑定方法
2.在对象被python系统回收之前触发,即:删除对象时
场景:脚本文件运行完python系统就会回收所有的变量,包括类的对象,如果此时在类中开启了一个需要操作系统才能回收掉的资源(如:发开文件),就需要在触发__del__函数体内将打开的文件关闭
-
__call__(self, *args, **kwargs)
1.对象绑定方法
2.调用对象或调用类时触发,即:obj(),class 类名()
3.__call__的返回值就是obj
注意: class 类名() 时,触发的是元类中的__call__
发生了三件事:
1). 调用object.__new__ 得到一个空对象obj
obj=self.__new__(self)
2). 调用obj.__init__,将空对象和参数传进去
self.__init__(obj, *args, **kwargs)
3). 将obj return出来
return obj
场景: 用来自定义类的创建过程
-
__doc__
类名.__doc__ 可以查看类中写的文档注释
-
__new__(self)
类加括号时,用来产生一个空对象的
-
__slots__
何禁止从外部修改实例属性
我们可以通过 __slots__ 来严格防止别人访问定义的实例属性
Python在存储实例的过程实际上是存储到 __dict__ 字典里
__slots_ 的作用就是一旦在类里定义了该属性,那么Python将再不会创建dict了,而是把实例属性存储到
__slots__ 里面(实际上是定义了一个描述器来存储)
-
用点访问属性时
__getattr__ 用点访问属性时 如果属性不存在时执行
__setattr__ 用点访问属性时
__delattr__ 用 del 对象.属性 删除属性时执行
-
用[ ]访问属性时
__getitem__ 当你用中括号取获取属性时 执行
__setitem__ 当你用中括号设置属性时 执行
__delitem__ 当你用中括号删除属性时 执行
-
__getattribute__
# 回顾__getattr__
class Foo:
def __init__(self,x):
self.x=x
def __getattr__(self, item):
print('执行的是我')
# return self.__dict__[item]
f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__
# __getattribute__
class Foo:
def __init__(self,x):
self.x=x
def __getattribute__(self, item):
print('不管是否存在,我都会执行')
f1=Foo(10)
f1.x
f1.xxxxxx
# 两者同时出现
#_*_coding:utf-8_*_
__author__ = 'xxx'
class Foo:
def __init__(self,x):
self.x=x
def __getattr__(self, item):
print('执行的是我')
# return self.__dict__[item]
def __getattribute__(self, item):
print('不管是否存在,我都会执行')
raise AttributeError('哈哈')
f1=Foo(10)
f1.x
f1.xxxxxx
#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
补充
-
isinstance(要判断的对象, 要判断的类型)
判断一个对象是否是某个类的实例
def num(a, b):
if isinstance(a, int) and isinstance(b, int):
return a + b
return None
print(num(20, 50))
-
issubclass(子类, 父类)
判断一个类是否是类一个类的子类
class Animal:
def eat(self):
print('吃')
class Pig(Animal):
def eat(self):
print('from pig')
class Tree:
def eat(self):
print('光合作用')
def mange(obj):
if issubclass(type(p), Animal):
obj.eat()
else:
print('非动物')
p = Pig()
t = Tree()
mange(p)
mange(t)