day11 - day 13
面向对象程序设计有三大特征:封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)
封装(Encapsulation):类包含了数据和方法,将数据和方法放在一个类中就构成了封装
继承(Inheritance):python支持多继续
多态(Polymorphism):指一类事物有多种形态,比如动物类,可以有猫,狗,猪等等。(一个抽象类有多个子类,因而多态的概念依赖于继承)
1. 类:抽象描述一类事物,是这一类事物共有的特征;对象:具体存在的事物,可以有很多个同类的事物;
2. class 类名():一般类名是大驼峰命名规则;类中的self代表对象本身,即是当前实例化对象;
3. 类中的__init__(self,参数)方法
1> 魔法方法,在实例化对象的同时,自动调用,做初始化的动作;
2> 因为实例化的同时自动调用了这个方法,故实例化时就要传递这个方法的参数过来:实例名=类名(__init__方法的参数);
3> 因为实例化的同时自动调用了这个方法,故实例化对象就拥有了__init__()定义的属性;
4> 实例对象想使用一般的类方法的属性时,是实例化对象需要先调用该方法,之后才会拥有这个方法中的属性;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Person: 2 3 id = '001' 4 5 def __init__(self,name,sex,city,age=18): # 实例化的同时,自动被调用,故实例化后,实例对象也就拥有了这些属性 6 self.my_name = name 7 self.my_sex = sex 8 self.my_city = city 9 self.my_age = age 10 11 def set_info(self,work,salary): # 实例对象想拥有这个方法中的属性时,实例对象必须先调用这个方法,才能拥有这些属性 12 self.my_work = work 13 self.salary = salary 14 15 def eat(self,food=None): 16 print("我喜欢吃{}".format(food)) 17 18 def study(self,kill): 19 print(f"我学习了{kill}。") 20 21 22 ran = Person('ran','女','中国') 23 print(ran.my_name) 24 # print(ran.my_work) # 报错 AttributeError: 'Person' object has no attribute 'my_work' 25 26 ran.set_info('测试','1W') # 实例对象调用类方法 27 print(ran.my_work)
4. 某个实例对象修改了类属性时,只影响它自己,其他实例对象调用该类属性时仍为原值;
5.一般不建议在类的方法中,动态的增加属性,因为只有这个方法被调用后,这个属性才会存在,其它情况下去引用就会报错,规范的做法是在__init__()方法中定义属性;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 抽象描述 - 定义 2 class Person: 3 4 id = '001' 5 6 def __init__(self,name,sex,city,age=18): 7 self.my_name = name # 给对象添加my_name属性,并给它赋值。 8 self.my_sex = sex 9 self.my_city = city 10 self.my_age = age 11 12 # 实例化对象的时候,对象是谁,self就是谁 13 # 在定义类的时候,并不知道对象是谁,用self来统一表示对象 14 def eat(self,food=None): 15 print("我喜欢吃{}".format(food)) 16 17 def study(self,kill): 18 print(f"我学习了{kill}。") 19 20 def cooking(self,**menu): 21 print(f"我会做{menu}。") 22 23 24 def find_another_friend(self,has_friend,food=None): 25 """ 26 年龄 > 18 ,以及 没有对象的,就找对象。否则就不找。 27 :param has_friend: True或者False.True表示有对象。False表示没有对象 28 :return: 29 """ 30 if self.my_age >= 18 and has_friend is False: 31 print("{} 需要找对象".format(self.my_name)) 32 # 告诉别人我喜欢吃什么 33 self.eat(food) 34 # 不建议。动态添加的属性,只有在调用此方法之后才会有。其它情况下如何去引用就会报错。 35 # self.is_dog = True # 在方法内部,给对象动态添加属性 36 37 else: 38 print("没有资格找对象。") 39 40 def play(self,game): 41 print(f"我会做{game}运动。") 42 43 44 ran = Person('ran','女','中国') # 实例化:对象名 = 类名(),此处self指代的是ran 45 ran.eat("臭豆腐") # 实例调用类方法: 对象名.方法名() 46 print(ran.id) # 实例调用类属性 47 print(ran.my_name) # 实例调用实例属性 48 ran.id = '001_ran' # 实例修改类属性,但是只影响自己,不会影响其他对象的这个值 49 print(f'ran修改了实类属性id的值:{ran.id}') 50 51 52 bql = Person("冰淇淋", "女", "新加坡") 53 bql.find_another_friend(False,"榴莲") 54 print(f'bql的类属性id值没有受影响:{bql.id}')
6. 某个实例对象给自己增加了实例属性,则该属性仅是该实例拥有,其他实例对象无法使用;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Person: 2 3 id = '001' 4 5 def __init__(self,name,sex,city,age=18): 6 self.my_name = name # 给对象添加my_name属性,并给它赋值。 7 self.my_sex = sex 8 self.my_city = city 9 self.my_age = age 10 11 # 实例化对象的时候,对象是谁,self就是谁 12 # 在定义类的时候,并不知道对象是谁,用self来统一表示对象 13 def eat(self,food=None): 14 print("我喜欢吃{}".format(food)) 15 16 ran = Person('ran','女','中国') 17 ran.height = 1.80 18 print(f'ran的身高:{ran.height}') 19 20 bql = Person("冰淇淋", "女", "新加坡") 21 print(f'bql的身高:{bql.height}') # 报错,因为仅ran实例对象有这个属性height
7. 实例属性:只有对象可以访问,类中定义是self.属性 = value,访问是 对象名.属性;
类属性:类和实例都可以访问,类中定义是 属性 = value,访问是 类名.属性 或 对象名.属性;
对象访问属性时,如果自己有就访问自己的,自己没有时,才会去访问类的;如果都没有,则会报错;
8. 类属性怎么修改? 使用类方法装饰器 @classmethod,cls代表当前类,与实例方法的区别:
1> 实例方法一般用 self 代表实例对象,一般是给实例对象调用的;
2> 类方法@classmethod:一般用 cls 代表当前类,类和对象都可以调用,不过一般是类去调用;
3> 静态方法@staticmethod:没有类方法或实例方法一样必传的self或cls,它就是普通的函数,只是定义在类里面,类和对象都可以调用;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import random 2 3 class Person: 4 5 color = "黄种人" # 类属性 = 值 6 7 def __init__(self,name,sex,age,color): 8 self.name = name 9 self.sex = sex 10 self.age = age 11 self.color = color # 对象的color属性 12 13 @classmethod # 类方法装饰器 14 def update_class_color(cls): 15 cls.color = "全世界的人" # 修改了类属性color 16 print(f'类的color为:{cls.color}') 17 18 def study(self, kill): # 实例方法 19 print(f"我学习了{kill}。") 20 21 def update_color(self): 22 self.color = '中国人' 23 print(f'对象的color为:{self.color}') 24 25 @staticmethod 26 def get_weather(): 27 weather = ["晴天", "小雪", "阴天", "小雨", "雷阵雨", "起风"] 28 index = random.randint(0, len(weather) - 1) 29 return weather[index] 30 31 print(f'类的color:{Person.color}') 32 Person.update_class_color() # 类名.类方法() 33 34 wh = Person('whm','男',20,'黑皮肤') 35 print(f'实例方法的color为:{wh.color}') 36 37 print(Person.get_weather()) # 类调用静态方法 38 print(wh.get_weather()) # 实例对象调用静态方法
9. 私有化:
1> _属性/_方法:约定俗成的,通过一个下划线开头,意思就是别访问我,我是私有化,但实际是可以通过 对象. _属性/_方法 的方式访问,全靠自觉;
2> __属性/__方法:深度私有化,不可以访问(通f过 对象.__属性/__方法 的方式访问不到),但是在类的内部是可以访问的,
所有如果类内部定义了访问私有 属性的方法,可以通过调用这个方法,从而得到私有属性的值;
3> _和__私有化方式不仅仅是在类属性和类方法中,同样在模块(.py)当中也同理, 模块内私有,仅想在模块内使用,其它模块导的时候,
表示这些不想被外部访问;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 _hello = "world" # 模块私有 2 3 def __hello(): # 模块私有 4 print("我不想其它的模块访问") 5 6 class Person: 7 8 __flag = True # 深度私有属性 9 10 def __init__(self, name, sex, city, age=18): 11 self.my_name = name # 给对象添加name属性,并给它赋值。 12 self.my_sex = sex 13 self.my_city = city 14 self.my_age = age 15 self.color = "中国人" # 对象的color属性 16 self._private_money = 2000 17 self.__spri_money = 4000 18 19 # 如果类内部定义了访问私有属性的方法,可以通过调用这个方法,从而得到私有属性的值 20 def get_spri_money(self): 21 print(self.__spri_money) # 类内部可以访问 22 23 24 wh = Person('whm','男','中国') 25 print(f'浅度私有:{wh._private_money}') # 可以访问到 26 print(f'深度私有:{wh.__spri_money}') # 报错:AttributeError: 'Person' object has no attribute '__spri_money'
10. 什么情况下使用私有化?
一般是实现大功能时,对外只需要知道该整体大功能,而对于里面的小功能方法对于使用来说不重要,不需要使用,此时这些小功能的方法就可以私有化;
11. python模块中的 __all__ ,该变量的值是一个列表,存储的是当前模块A中一些成员,如属性,方法或者类的名称
1> 当该模块A被其他模块B以 from 模块A import * 形式导入时,模块B只能使用__all__列表中指定的成员,未指定的成员是无法导入的;
2> 若没有定义__all__,则会导入模块A内的所有公有属性,方法和类;
moduleA.py模块代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import random 2 3 __all__ = ['say'] 4 5 def say(): 6 print("人生苦短,我学Python!") 7 def CLanguage(): 8 print("C语言中文网:http://c.biancheng.net") 9 def disPython(): 10 print("Python教程:http://c.biancheng.net/python")
moduleB.py模块代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import examp.moduleA as ma # 此时__all__不起作业 2 ma.say() 3 ma.CLanguage() 4 ma.disPython() 5 6 from examp import moduleA # 此时__all__不起作业 7 moduleA.say() 8 moduleA.CLanguage() 9 moduleA.disPython() 10 11 from examp.moduleA import * # 此时__all__起作业 12 say() 13 # CLanguage() # 报错 NameError,因为__all__未指定该方法,故这个方法在此不能被调用到 14 # disPython() # 报错 NameError,因为__all__未指定该方法,故这个方法在此不能被调用到
12. 继承:子类继承父类,就拥有了父类所有的属性和方法,除了私有化的;
1> object是所有类的基类,python3默认所有类都会继承object;
2> _属性/_方法: 子类可以继承,但是建议一般子类不调用;
3> __属性/__方法:深度私有化,子类不可以继承;
4> python可以多继承,语法 class 子类(父类1,父类2...);
5> 子类想在父类的功能或属性以外,子类可以增加自己特色的属性和方法;
13. 重写:子类在内部定义了一个与父类中同名的方法,即为重写;
场景:子类想在父类原有功能的基础上,去优化或改变父类的方法,
1> 不动父类的功能,只是增加一些额外的功能,即子类可以部分重写父类方法;
a> 在子类内部的与父类同名方法的内部,想调用父类同名的方法,直接使用self.同名方法()会报 递归错误,因为self代表的是子类;
b> 在子类内部的与父类同名方法的内部,想调用父类同名的方法,使用 super() 超类,super代表父类;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Father: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def ear_money(self,work): 7 print(f"{self.name}通过{work}来挣钱。") 8 9 def hobby(self,favirate): 10 print("{} 的爱好是 {}".format(self.name,favirate)) 11 12 class Son(Father): 13 14 def study(self,content): # 在父类的功能或属性外,添加属性子类自己的特色方法 15 print("{}通过学习{}来提升自己。".format(self.name,content)) 16 17 18 def ear_money(self,work): # 部分重写 19 # 此时实例对象调用了自己的ear_money()方法,而不会调用父类的该方法。 20 # 会报错 递归错误:RecursionError: maximum recursion depth exceeded 21 # self.ear_money(work) 22 23 # 在与父类同名方法内,想调用父类该同步方法 24 super().ear_money(work) # 超类,super代表父类 25 print(f'{self.name}通过投资理财再赚了2000块。') 26 27 28 whm = Son('whm',20) 29 whm.ear_money('python') # 用的父类方法
2> 推翻父类的功能,重新写一遍,即子类可以完全重写父类方法,不用父类的代码;
a> 子类完全重写父类同名方法时,方法的参数可以不一样,方法可以被正常调用;
b> 但是不符合python的编码规范,会有提示: Signature of method 'Son.ear_money()' does not match signature of base method in class 'Father' ;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Father: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def ear_money(self,work): 7 print(f"{self.name}通过{work}来挣钱。") 8 9 def hobby(self,favirate): 10 print("{} 的爱好是 {}".format(self.name,favirate)) 11 12 class Son(Father): 13 14 def study(self,content): # 在父类的功能或属性外,添加属性子类自己的特色方法 15 print("{}通过学习{}来提升自己。".format(self.name,content)) 16 17 18 def ear_money(self,work1,work2): # 完全重写 19 # 虽然与父类同名,但是我要完全重写,不用父类的代码 20 print(f'{self.name}通过{work1}和{work2}来赚米米。。') 21 22 whm = Son('whm',20) 23 24 whm.ear_money('搬砖','写作')
c> 所以一般子类在重写父类方法时,规范的做法是参数保持一致;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Father: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def ear_money(self,work): 7 print(f"{self.name}通过{work}来挣钱。") 8 9 def hobby(self,favirate): 10 print("{} 的爱好是 {}".format(self.name,favirate)) 11 12 class Son(Father): 13 14 def study(self,content): # 在父类的功能或属性外,添加属性子类自己的特色方法 15 print("{}通过学习{}来提升自己。".format(self.name,content)) 16 17 def ear_money(self,work): # 完全重写,符合规范 18 # 虽然与父类同名,但是我要完全重写,不用父类的代码 19 print(f'{self.name}通过{work}来赚米米,还帮助了更多人。') 20 21 whm = Son('whm',20) 22 whm.ear_money('开公司') #用的子类完全重写的方法
3> 总结:子类调用方法时,自己有该方法时就调用用自己的,否则就会使用父类的该方法;
14. 重载:python没有重载,因为重载的场景是 参数个数不一样时 或 参数类型不一样时,需要重载,但是对于python本来就支持不定长参数,而python传参时没有限制类型;
15. isinstance(实例名,类名):判断某个对象,是否为类的实例
16. 多态:一类事物有多种形态,如excel和word文档,都是文件,python是一门动态强数据性的语言,天生支持多态;
1> 函数(obj:类型):如 函数(obj: int) 代表调用这个函数时,指示参入int类型的参数,但是不是说只能传入int,其他类型也是可以的;
2> python不会限制传给函数参数的数据类型,不像其他语言都是申明定义好了具体的数据类型;
3> 鸭子类型:不管是什么类对象,只要会叫,就认为是鸭子在叫,这种就叫鸭子类型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class People: 2 def run(self): 3 print("人类在跑") 4 5 class Dog(People): 6 def run(self): 7 print("狗在跑") 8 9 class Cat(People): 10 def run(self): 11 print("猫在跑") 12 13 class Car: 14 def run(self): 15 print("车在跑") 16 17 # python: 函数参数不做类型限制,这里只是指示参入People类一样的数据类型,但不是说只能是这个类,只要对象有run方法的,就可以传入正常使用 18 def who_can_run(obj: People): # 鸭子类型:不管是什么类对象,只要会叫,就认为是鸭子在叫,这种就叫鸭子类型 19 obj.run() 20 21 # python 没有多态 、 处处皆多态 -- 鸭子类型(自行了解) 22 mycar = Car() 23 who_can_run(mycar) 24 25 mycat = Cat()
17. python自省是获取对象的能力,而反射是操纵对象的能力;
反射机制,利用字符串的形式在模块或对象中操作(查找/获取/删除/添加)成员,即在类定义之外,查找/获取/添加/删除属性,这些属性是在代码执行过程当中,动态操作属性;
1> hasattr(类/对象, 属性):类/对象 是否有某个属性
2> setattr(类/对象, 属性, 值):给类/对象 设置某个属性(有则修改,无则添加)
3> getattr(类/对象, 属性):获取类/对象 某个属性的值,若类/对象没有该属性时,直接获取会报错 AttributeError
4> delattr(类/对象, 属性):删除类/对象 某个属性
PS:Python中使用delattr()和setattr()实现反射,而其他方法则属于自省
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Person: 2 3 def __init__(self, name, sex): 4 self.my_name = name 5 self.my_sex = sex 6 self.color = "中国人" 7 8 def update_color(self): 9 self.color = "中国汉族" 10 print("对象的color为:{}".format(self.color)) 11 12 xj = Person("小简", "女") 13 14 # # 获取对象的hobby属性 15 # value = getattr(xj,"hobby") 16 # print("之前:", value) # 报错:AttributeError 17 18 # 如果对象xj没有hobby属性,就给它添加一个 19 if not hasattr(xj, "hobby"): 20 setattr(xj,"hobby","听歌") 21 22 # 获取对象的hobby属性 23 value = getattr(xj,"hobby") 24 print("之后:", value) 25 26 # 删除hobby属性 27 delattr(xj, "hobby") 28 29 # # 获取对象的hobby属性 30 # value = getattr(xj,"hobby") 31 # print("删了之后:", value) # 报错: AttributeError,因为上面删除了该属性