一、面向对象
面向对象的三大特性:
封装、继承、多态
1. 函数式编程和面向对象的对比:
举例:开发一个消息提醒的功能(邮件/短信/微信)
函数:
def email(em, text): """ 发送邮件 :return: """ print(em, text) def msg(tel, text): """ 发送短信 :return: """ print(tel, text) def wechat(num, text): """ 发送微信 :return: """ print(num, text) # 编写功能:向所有的联系方式发送天气 if True: msg('188888888', '今天有小雨') email('hao123@163.com.com', '今天有小雨') wechat('xxxx', '今天有小雨')
面向对象:
class Message: def email(self, em, text): """ 发送邮件 :return: """ print(em,text) def msg(self, tel, text): """ 发送短信 :return: """ print(tel,text) def wechat(self, num, text): """ 发送微信 :return: """ print(num,text) # 编写功能:向所有的联系方式发送天气 if True: obj = Message() obj.email('hao123@163.com', '今天有小雨') obj.msg('188888888', '今天有小雨') obj.wechat('xxxx', '今天有小雨')
对比:
函数:定义简单、调用简单
面向对象:定义复杂、调用复杂 好处:归类,将某些类似的函数功能写在一起
总结:
1 函数式编程可能会比面向对象好
2. Python中支持两种编程方式
3. 面向对象方式格式:
定义:
class 类名: # 定义了一个类
def 函数名(self): # 在类中编写一个“方法”
pass
调用:
x1 = 类名() # 创建了一个对象(实例化一个对象)
x1.函数名() # 通过对象调用其中一个方法
构造方法:
class Foo:
def __init__(self, name): # 构造方法,目的进行数据初始化
self.name = name
self.age = 18
obj = Foo("久末") # 给类名加括号,默认调用构造方法
通过构造方法,可以将数据进行打包,以后使用时,去其中获取即可
应用:
1、将数据封装到对象中,以供自己在方法中使用
class FileHandler: def __init__(self, file_path): self.file_path = file_path self.f = open(self.file_path, 'rb') def read_first(self): # self.f.read() # ... pass def read_last(self): # self.f.read() # ... pass def read_second(self): # self.f... # ... pass obj = FileHandler('C:/xx/xx.log') obj.read_first() obj.read_last() obj.read_second() obj.f.close()
2、将数据封装到对象中,供其他函数调用
class FileHandler: def __init__(self, file_path): self.file_path = file_path self.f = open(self.file_path, 'rb') def read_first(self): # self.f.read() # ... pass def read_last(self): # self.f.read() # ... pass def read_second(self): # self.f... # ... pass obj = FileHandler('C:/xx/xx.log') obj.read_first() obj.read_last() obj.read_second() obj.f.close()
2. 面向对象如何编写:
方式一:先归类,然后提取公共值
方式二:先在指定类中编写和当前类相关的所有代码,再提取公共值
三大特性:
封装:
将相关功能封装到一个类中
将数据封装到一个对象中
继承:
继承是为了复用,提高代码得重用性
先找子类(派生类),后找父类(基类)—— 子类和父类是相对存在的
先从子类中找,没有就从父类找
多继承(只存在python中的功能):左边更亲
多态:
多种形态或者多种状态
鸭子模型:只要可以嘎嘎嘎叫的就是鸭子
二、类成员
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份
class Foo: # 方法 def __init__(self, name): # 实例变量/字段 self.name = name # 方法 def func(self): pass # obj,Foo类的对象 # obj,Foo类的实例 obj = Foo('jiumo')
1. 变量(字段):
字段包括实例变量和静态变量,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同
由上图可是:
- 静态变量在内存中只保存一份
- 实例变量在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的变量,那么就使用静态变量
# 类变量/实例变量 class Foo: # 类变量/静态字段 country = '中国' # 方法 def __init__(self, name): # 实例变量/字段 self.name = name def func(self): pass obj1 = Foo('jiumo') obj2 = Foo('王XX') obj1.country = '美国' print(obj1.country) print(obj2.country) obj1 = Foo('jiumo') obj2 = Foo('王XX') Foo.country = '美国' print(obj1.country) print(obj2.country) ==>美国 中国 美国 美国
实例变量(字段)
- 公有实例变量(字段)
# 公有实例变量(字段) class Foo: def __init__(self, name): self.name = name self.age = 123 # 内部调用 def func(self): print(self.name) obj = Foo("jiumo") # 外部调用 print(obj.name) # jiumo print(obj.age) # 123 obj.func() # jiumo
- 私有实例变量(字段)
# 私有实例变量(字段) class Foo: def __init__(self, name): # 定义为私有 self.__name = name self.age = 123 # 内部调用 def func(self): print(self.__name) obj = Foo("jiumo") # 外部不可直接调用 # print(obj.name) # 报错AttributeError: 'Foo' object has no attribute 'name' print(obj.age) obj.func() # 间接访问:让func帮助执行内部私有的__name 就相当在类的内部有认识的”关系” 间接的得到想要的内容
类变量(静态字段)
- 公有类变量(静态字段)
# 默认公有类变量(字段) class Foo: country = "中国" def __init__(self): pass def func(self): # 内部调用 print(self.country) print(Foo.country) # 推荐 # 外部调用 print(Foo.country) # 中国
- 私有类变量(静态字段)
# 私有 class Foo: __country = "中国" def __init__(self): pass def func(self): # 内部调用 print(self.__country) print(Foo.__country) # 推荐 # 外部无法直接调用 # print(Foo.__country) obj = Foo() obj.func(
准则:
实例变量(字段)访问时,使用对象访问,即:obj1.name
类变量(静态字段)访问时,使用类访问,即:Foo.country(实在不方便时才使用对象)
易错点:
obj1 = Foo('jiumo') obj2 = Foo('王XX') obj1.name = '老王' print(obj1.name) print(obj2.name) ==>老王 王XX
什么时候用类变量:
当所有对象中有共同的字段,并且值永远同步,那么可以使用类变量
父类中的私有字段子辈无法知道,但是可以通过调用父类得到
# 子类也不能直接调用父类的私有字段 class Bsae(object): __secret = "RMB" class Foo(Bsae): def func(self): # print(self.__secret) # AttributeError: 'Foo' object has no attribute '_Foo__secret' print(Foo.__secret) # AttributeError: type object 'Foo' has no attribute '_Foo__secret' obj = Foo() obj.func() # 同样可以利用间接调用的方法得到私有字段 class Bsae(object): __secret = "RMB" def money(self): print(Bsae.__secret) class Foo(Bsae): def func(self): pass obj = Foo() obj.money()
2. 方法
实例方法
静态方法
1. 定义时:
- 方法上方写 @staticmethod
- 方法参数可有可无
2. 调用时:
- 类.方法名() # 推荐使用
- 对象.方法名()
3. 什么时候使用静态方法:
- 无需调用对象中封装的值
- 类方法
1. 定义时:
- 方法上方写:@classmethod
- 方法的参数:至少有一个cls参数
2. 执行时:
- 类名.方法名 # 默认会将当前类传到参数中
3. 什么时使用类方法:
- 如果在方法中会使用到当前类,那么就可以使用类方法
# 没必要写实例方法 class Foo(object): def __init__(self, name): self.name = name def func(self): print('实例方法') # 没有用到构建方法中的值 obj = Foo('jiumo') obj.func() # 有意义的实例方法 class Foo(object): def __init__(self, name): self.name = name # 实例方法,最少有一个参数 def func(self): print(self.name) # wb # 静态方法,可以没有参数. 如果方法中无需使用对象中封装的值,那么就可以使用静态方法 # 可以通过类直接调用,不需要实例化类这部操作 @staticmethod def display(): print('静态方法') # 静态方法 # 类方法 @classmethod def train(cls, name): print(cls) # <class '__main__.Foo'> print(name) # 类方法 # 实例方法 obj = Foo('wb') obj.func() # 静态方法的调用 Foo.display() # 类方法的调用 Foo.train('类方法') ==>实例方法 wb 静态方法 <class '__main__.Foo'> 类方法
方法的成员修饰符:方法也有公有方法和私有方法之分 用法同变量的成员修饰符
# 私有的实例方法 class Foo(object): def __init__(self): pass def __display(self,arg): print('私有方法',arg) def func(self): self.__display(123) obj = Foo() # obj.__display(123) # 无法访问 obj.func() # 私有的静态方法 class Foo(object): def __init__(self): pass @staticmethod def __display(arg): print('私有静态 方法',arg) # 私有静态 方法 123 def func(self): Foo.__display(123) @staticmethod def get_display(): Foo.__display(888) #私有静态 方法 888 # Foo.__display(123) 报错 obj = Foo() obj.func() Foo.get_display()
3. 属性
class Foo(object): def __init__(self): pass @property def start(self): return 1 @property def end(self): return 10 obj = Foo() print(obj.start) print(obj.end)
1 编写时:
- 方法上方写@property
- 方法参数:只有一个self参数
2 调用时:
- 无需加括号 对象.方法
3 应运场景:
- 对于简单的方法,当无需传参且有返回值时
4. 特殊方法
class Foo(): # 1. def __init__(self,a1, a2): self.a1 = a1 self.a2 = a2 # 2. def __call__(self, *args, **kwargs): print('wb', args, kwargs) return 123 # 3. def __getitem__(self, item): print(item) return 123 # 4. def __setitem__(self, key, value): # 无返回值 print(key, value, 123) # 5. def __delitem__(self, key): # 无返回值 print(key) # 6. def __add__(self, other): return self.a1 + other.a1 # 7. def __enter__(self): print('开始代码') return 999 # 8. def __exit__(self, exc_type, exc_val, exc_tb): print("结束代码") 1.类名() 自动执行 __init__ obj = Foo(1, 2) 2.对象() 自动执行 __call__ ret = obj(2018, 9, time = 2) print(ret) 3.对象[] 自动执行 __getitem__ ret = obj['wang'] print(ret) 4.对象['xx'] = 11 自动执行 __setitem__ obj['k1'] = 123 5.del 对象['xx'] = 11 自动执行 __delitem__ del obj['wang'] 6.对象+对象 自动执行 __add__ 9. with 对象 自动执行__enter__ 和 __exit__ obj = Foo(1, 2) with obj as f: print(f) print('内部代码') 10.真正的构造方法 __new__ class Foo(object): def __init__(self,a1, a2): print(1) self.a1 = a1 self.a2 = a2 def __new__(cls, *args, **kwargs): print(2) # 执行到此处中断了 Foo(1, 2)
三、反射
python中的反射功能是由以下四个内置函数提供的:
- getattr(): 根据字符串为参数(第二个参数),去对象(第一个参数)中去寻找与之同名的成员
- hasattr():根据字符串的形式,去判断对象中是否有成员
- setattr():根据字符串的形式,动态的设置一个成员(内存)
- delattr():根据字符串的形式,动态的删除一个成员(内存)
class Foo(object): def __init__(self): self.name = 'jiumo' def func(self): return 'func' obj = Foo() # 检查是否含成员变量 print(hasattr(obj, 'name')) # True print(hasattr(obj, 'func')) # True # 获取成员 print(getattr(obj, 'name')) # jiumo print(getattr(obj, 'func')) # <bound method Foo.func of <__main__.Foo object at 0x0000016810C9C908>> # 设置成员 setattr(obj, 'age', 18) print(getattr(obj, 'age')) # 18 setattr(obj, 'show', lambda num: num + 1) # print(getattr(obj, 'show')) # <function <lambda> at 0x0000016811EA47B8> # 删除成员 delattr(obj, 'name') print(hasattr(obj, 'name')) # Fals delattr(obj, 'show') print(hasattr(obj, 'show')) # False # 反射实例说明: class Foo(object): func_lst = ['f1', 'f2', 'f3'] def f1(self): print('注册成功') def f2(self): print('登陆成功') def f3(self): print('注销成功') obj = Foo() while True: print(""" 选择需要的功能: 1. 注册 2. 登陆 3. 注销 """) val = int(input("请输入要选择的功能:")) try: func_name = obj.func_lst[val-1] if hasattr(obj, func_name): func = getattr(obj, func_name) func() break except Exception: print("请输入正确的序号!")