上篇回顾
- 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用
- 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中)
- 对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数
- 面向对象三大特性:封装、继承和多态
- 静态属性(@property) 特点:将函数属性伪装成数据属性(封装逻辑)
- 静态方法(@staticmethod) 跟类和实例无关系,名义上归属类管理,但不能使用类变量和实例变量
- 类方法(@classmethod) 跟实例没有关系,类自己调用;只能访问类的属性,不能访问实例属性,不需要self参数,自动加cls参数
- 面向对象的专业术语
本篇介绍
反射
- 根据字符串的形式去某个对象中操作它的成员
- 四个可以实现反射的函数(也适用于对象和类)
class Sea: # def __init__(self,name,country,addr): self.name = name self.country = country self.addr = addr def sea_wave(self): print("一波儿海啸正在来袭") s1 = Sea("东海","哥雅王国","风车村") print(hasattr(s1,"name")) # 判断有没有 print(getattr(s1,"name123")) # 找不到报错 print(getattr(s1,"name123",None)) # 可以设置返回值则不报错 del s1.name # 方式1 print(s1.__dict__) delattr(s1,"name") # 方式2 print(s1.__dict__) setattr(s1,"age",10000) # 设置一个东西 print(s1.__dict__)
为什么用到反射(举个简单的小例子)
有俩程序员,一个alex,一个是egon,alex在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,alex想到了反射,使用了反射机制alex可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现alex想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
1
2
3
4
5
|
class FtpClient: \'ftp客户端,但是还么有实现具体的功能\' def __init__( self ,addr): print ( \'正在连接服务器[%s]\' % addr) self .addr = addr |
#from module import FtpClient f1=FtpClient(\'192.168.1.1\') if hasattr(f1,\'get\'): func_get=getattr(f1,\'get\') func_get() else: print(\'---->不存在此方法\') print(\'处理其他的逻辑\') 不影响alex的代码编写
三个参数,给对象添加属性
这是python解释器底层的内置方法。当然我们也可以对它们进行一些操作
__setattr__ 添加/修改属性会触发它的执行
__delattr__ 删除属性的时候会触发
__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
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): # __getattr__常用,且需要记忆 print(\'执行__getattr__\') f1=Foo(10) print(f1.y) #没有的时候就会触发: __getattr__ print(getattr(f1,\'y\')) #len(str)---->str.__len__() f1.ssssssssssssssssssssssssssssss 10 10 执行__getattr__
动态导入模块
1、新建一个t.py的文件
print(\'---------------->\') def test1(): print(\'test1\') def _test2(): print(\'test2\')
2、再创建:m1文件夹,再在他下面创建一个t.py
module_t=__import__(\'m1.t\') print(module_t) module_t.t.test1() # from m1.t import * # from m1.t import test,_test2 import importlib m=importlib.import_module(\'m1.t\') print(m) m.test1() m._test2()
----------------> <module \'m1\' (namespace)> test1 <module \'m1.t\' from \'D:\\\\python\\\\day26\\\\m1\\\\t.py\'> test1 test2
二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
1 # 二次加工标准类型 2 class List(list): 3 def append(self,p_object): 4 if type(p_object) is str: 5 super().append(p_object) 6 else: 7 print("添加的类型必须为字符串类型") 8 9 l = List(["Python之路:面向对象(进阶)