Python:反射和面向对象(一)
-
反射
在所有语言里面都有这个名词:反射。反射也就是:通过字符串的形式,导入模块。通过字符串的形式,去模块中寻找指定函数,并执行。Python中的反射功能是由以下四个内置函数提供:
hasattr(模块,"成员"):根据字符串的形式去某个模块中检查是否含有某成员
getattr(模块,"成员"):根据字符串的形式去某个模块中获取成员
setattr(模块,"成员"):根据字符串的形式去某个模块中设置成员
delattr(模块,"成员"):根据字符串的形式去某个模块中删除成员
正常导入模块:
首先定义一个模块,模块里面有几个函数:
然后导入模块,执行函数:
import commons r = commons.f1() print(r) #打印结果: f1 F1
此时有个需求:根据用户输入的内容,导入模块
#用户输入什么我们就拿到哪个字符串 inp = input("请输入模块:") print(inp,type(inp)) #打印结果: 请输入模块:commons commons <class 'str'>
再根据用户输入的内容,导入模块,执行函数:
之前我们学过__import__(字符串):用于以字符串的形式去某个模块中找函数,so:
#import commons as CC #本质:__import__ #DD = __import__("commons") #应用通过字符串形式导入模块: inp = input("请输入模块:") print(inp,type(inp)) r = __import__(inp) #模块中寻找函数并且执行函数 #此时f1()还并不是字符串 ret = r.f1() print(ret) #打印结果: 请输入模块:commons commons <class 'str'> f1 F1
现在根据字符串形式去模块中寻找函数并且执行函数:
inp_name = input("请输入模块:") inp_func = input("请输入要执行的函数:") #导入输入的模块 inp = __import__(inp_name) #获取模块中的函数名 target_func = getattr(inp,inp_func) #执行函数 ret = target_func() print(ret) #打印结果: 请输入模块:commons 请输入要执行的函数f1 f1 F1
# 查看是否存在,不存在False,存在True r = hasattr(commons,"NAME") print(r) # 设置成员 r = setattr(commons,"ARG",lambda a: a + 1) print(r) # 删除成员 delattr(commons,"NAME") #查看已删除成员是否还存在 r = hasattr(commons,"NAME") print(r)
注:
#设置None:如果找到成员f1,就会执行;如果没有找到,就会报错 target_func = getattr(commons,"Name",None) ret = target_func() print(ret)
扩展两种导入模块的方式:
a = __import__("模块名") a = __import__("文件名.文件名.模块名",fromlist=True)
示例:
from lib import account url = input("请输入url:") if url.endswith("login"): r = account.login() print(r) elif url.endswith("logout"): r = account.logout() print(r) elif url.endswith("nb"): r = account.nb() print(r) else: print("404")
from lib import account url= input("请输入url") inp = url.split("/")[-1] if hasattr(account,inp): target_func = getattr(account,inp) r = target_func() print(r) else: print("404")
#模块名/函数名 url = input("请输入url:") #模块名和函数名的格式 target_module,target_func = url.split("/") #导入用户输入的模块 m = __import__("lib."+target_module,fromlist=True) if hasattr(m,target_func): target_func = getattr(m,target_func) r = target_func() print(r) else: print("404")
总结:反射是通过字符串的形式去对象(某个模块)中操作其成员。一切事物皆对象!
-
面向对象基础
概述:
C#、Java只能用面向对象编程。Ruby、Python是可以用函数编程和面向对象。
- 面向过程编程:根据业务逻辑从上到下写代码
- 函数式编程:讲某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象编程:对函数进行分类和封装
面向过程编程:初学者最容易接受
while True: if cpu利用率 > 90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 if 硬盘使用空间 > 90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 if 内存占用 > 80%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接
函数式编程:增强代码的重用性和可读性
def 发送邮件(内容) #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 while True: if cpu利用率 > 90%: 发送邮件('CPU报警') if 硬盘使用空间 > 90%: 发送邮件('硬盘报警') if 内存占用 > 80%: 发送邮件('内存报警')
面向对象编程:是一种编程方式,需要使用“类”和“对象”来实现,所以,面向对象编程其实就是对“类”和“对象”的使用。
面向对象不是所有情况都适用
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
注:
- class是关键字,表示类
- 创建对象,在类名称后加括号即可
- 类中定义的函数叫做“方法”
class Foo: def Bar(self): print("Bar") def Hello(self): print("Hello") obj = Foo() obj.Bar() obj.Hello()
函数式编程:执行函数
面向对象编程:创建对象 通过对象执行方法
面向对象三大特性
面向对象的三大特性:封装、继承、多态
-
封装
封装,将内容封装到某个地方,以后再去调用被封装在某处的内容。所有在使用面向对象的封装特性时,需要:
使用场景:
1、当同一类型的方法具有相同参数时,直接封装到对象即可。
2、把类当做模板,创建多个对象(对象内封装的数据可以不一样)
第一步:将内容封装到某处
__init__:构造方法
class Foo: #####称为构造方法,根据类创建对象时自动执行 def __init__(self,name): self.country = "中国" self.Name = name def eat(self): print(self.Name + "eat") def sleep(self): print(self.Name + "sleep") #根据类Foo创建对象(或创建一个Foo类的实例) #自动执行Foo类的__init__方法 obj1 = Foo("zou") #根据类Foo创建对象(或创建一个Foo类的实例) #自动执行Foo类的__init__方法 obj2 = Foo("zhang") obj1.eat() obj2.sleep() #打印结果: zoueat zhangsleep
self是一个形式参数,当执行 obj1 = Foo("zou")时,self等于 obj1
当执行 obj2 = Foo("zhang")时,self等于 obj2
所以,内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有name属性。
第二步:从某处调用被封装的内容
调用被封装的内容时,有两种情况:
1、通过对象直接调用被封装的内容
class Foo: def __init__(self,name,age): self.country = "中国" self.Name = name self.Age = age def eat(self): print(self.Name + "eat") def sleep(self): print(self.Name + "sleep") #根据类Foo创建对象 #自动执行Foo类的__init__方法 obj1 = Foo("zou",18) #直接调用obj1对象的属性 print(obj1.Name) print(obj1.Age) #根据类Foo创建对象 #自动执行Foo类的__init__方法 obj2 = Foo("zhang",19) #直接调用obj2对象的属性 print(obj2.Name) print(obj2.Age) #打印结果: zou 18 zhang 19
2、通过self间接调用被封装的内容
class Foo: def __init__(self,name,age): self.Name = name self.Age = age def f(self): print(self.Name) print(self.Age) obj1 = Foo("zou",18) obj1.f() # Python默认会将obj1传给self参数,即:obj1.f(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 zou ;self.age 是 18 obj2 = Foo("zhang",19) obj2.f() # Python默认会将obj2传给self参数,即:obj2.f(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 zhang ;self.age 是 19
总结:对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。
练习:在终端输出如下信息
小明,10岁,男,上山去砍柴
小明,10岁,男,开车去东北
老李,90岁,男,上山去砍柴
老李,90岁,男,开车去东北
def kanchai(name, age, gender): print "%s,%s岁,%s,上山去砍柴" %(name, age, gender) def qudongbei(name, age, gender): print "%s,%s岁,%s,开车去东北" %(name, age, gender) kanchai('小明', 10, '男') qudongbei('小明', 10, '男') kanchai('老李', 90, '男') qudongbei('老李', 90, '男')
class Foo: def __init__(self, name, age ,gender): self.name = name self.age = age self.gender = gender def kanchai(self): print "%s,%s岁,%s,上山去砍柴" %(self.name, self.age, self.gender) def qudongbei(self): print "%s,%s岁,%s,开车去东北" %(self.name, self.age, self.gender) xiaoming = Foo('小明', 10, '男') xiaoming.kanchai() xiaoming.qudongbei() laoli = Foo('老李', 90, '男') laoli.kanchai() laoli.qudongbei()
上述对比可以看出,如果使用函数式编程,需要在每次执行函数时传入相同的参数,如果参数多的话,又需要粘贴复制了... 而对于面向对象只需要在创建对象时,将所有需要的参数封装到当前对象中,之后再次使用时,通过self间接去当前对象中取值即可。
-
继承
继承:面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
例如:
动物共同功能:吃、喝、睡
猫特有功能:喵喵叫
狗特有功能:汪汪叫
class Animals: def chi(self): print(self.Name + "吃") def he(self): print(self.Name + "喝") class Dog(Animals): def __init__(self,name): self.Name = name def jiao(self): print(self.Name + "汪") class Cat(Animals): def __init__(self,name): self.Name = name def jiao(self): print(self.Name + "喵") obj1 = Dog("小黑") obj1.chi() obj1.jiao() obj2 = Cat("小花") obj2.chi() obj2.jiao() #打印结果: 小黑吃 小黑汪 小花吃 小花喵
所以,对于面向对象的继承来说,就是将多个类共有的方法提取到父类(基类)中,子类(派生类)仅需继承父类(基类)而不必一一实现每个方法。
注:
派生类可以继承基类中所有的功能
派生类和基类某一方法同时存在,优先找派生类
class Animals: def chi(self): print(self.Name + "吃") def he(self): print(self.Name + "喝") def sleep(self): print(self.Name + "睡") class Uncle: def play(self): print(self.Name + "玩") def sleep(self): print("sleep") class Dog(Animals,Uncle): def __init__(self,name): self.Name = name def talk(self): print(self.Name + "说话") obj = Dog("小黑") obj.chi() obj.sleep() #打印结果: 小黑吃 小黑睡 #####################
class Animals: def chi(self): print(self.Name + "吃") def he(self): print(self.Name + "喝") def sleeping(self): print(self.Name + "睡") class Uncle: def play(self): print(self.Name + "玩") def sleep(self): print("sleep") class Dog(Animals,Uncle): def __init__(self,name): self.Name = name def talk(self): print(self.Name + "说话") obj = Dog("小黑") obj.chi() obj.sleep() #打印结果: 小黑吃 sleep ########## 查找顺序:Dog-->Animals-->Uncle
Python的类可以继承多个类,Java和C#中则只能继承一个类
Python的类如果继承了多个类,那么寻找方法的方式有两种(Python3中)
第一种:
class A: def f1(self): print("A") class B: def f1(self): print("B") class C(A): def f1(self): print("C") class D(B): def f1(self): print("D") class E(C, D): def f1(self): print("E") obj = E() obj.f() #查找顺序:E-->C-->A-->D-->B
顺序见图:
第二种:
class All(): def f(self): print("All") class A(All): def f1(self): print("A") class B(All): def f1(self): print("B") class C(A): def f1(self): print("C") class D(B): def f1(self): print("D") class E(C, D): def f1(self): print("E") obj = E() obj.f() # 查找顺序:E-C-A-D-B-All
顺序见图:
-
多态
Python不支持多态并且也用不到多态,多态的概念是应用于C#和Java这一类强类型语言中,而Python崇尚“鸭子类型”。
鸭子类型:
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' def Func(obj): print obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj)