003_python对象

day11 - day 13

    面向对象程序设计有三大特征:封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)

      封装(Encapsulation):类包含了数据和方法,将数据和方法放在一个类中就构成了封装

      继承(Inheritance):python支持多继续

      多态(Polymorphism):指一类事物有多种形态,比如动物类,可以有猫,狗,猪等等。(一个抽象类有多个子类,因而多态的概念依赖于继承)

1. 类:抽象描述一类事物,是这一类事物共有的特征;对象:具体存在的事物,可以有很多个同类的事物;

2.  class 类名():一般类名是大驼峰命名规则;类中的self代表对象本身,即是当前实例化对象;

3. 类中的__init__(self,参数)方法

        1> 魔法方法,在实例化对象的同时,自动调用,做初始化的动作;

        2> 因为实例化的同时自动调用了这个方法,故实例化时就要传递这个方法的参数过来:实例名=类名(__init__方法的参数);

        3> 因为实例化的同时自动调用了这个方法,故实例化对象就拥有了__init__()定义的属性;

        4> 实例对象想使用一般的类方法的属性时,是实例化对象需要先调用该方法,之后才会拥有这个方法中的属性;

 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__()方法中定义属性;

 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. 某个实例对象给自己增加了实例属性,则该属性仅是该实例拥有,其他实例对象无法使用;

 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,它就是普通的函数,只是定义在类里面,类和对象都可以调用;

 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)当中也同理, 模块内私有,仅想在模块内使用,其它模块导的时候,

           表示这些不想被外部访问;

 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模块代码如下:

 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")
moduleA.py

moduleB.py模块代码如下:

 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__未指定该方法,故这个方法在此不能被调用到
moduleB.py

12.  继承:子类继承父类,就拥有了父类所有的属性和方法,除了私有化的;

      1> object是所有类的基类,python3默认所有类都会继承object;

      2> _属性/_方法: 子类可以继承,但是建议一般子类不调用;

      3> __属性/__方法:深度私有化,子类不可以继承;

      4> python可以多继承,语法 class 子类(父类1,父类2...);

      5> 子类想在父类的功能或属性以外,子类可以增加自己特色的属性和方法;

13. 重写:子类在内部定义了一个与父类中同名的方法,即为重写;

        场景:子类想在父类原有功能的基础上,去优化或改变父类的方法,

       1> 不动父类的功能,只是增加一些额外的功能,即子类可以部分重写父类方法;

             a> 在子类内部的与父类同名方法的内部,想调用父类同名的方法,直接使用self.同名方法()会报 递归错误,因为self代表的是子类;

             b> 在子类内部的与父类同名方法的内部,想调用父类同名的方法,使用 super() 超类,super代表父类;

 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' ;

 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> 所以一般子类在重写父类方法时,规范的做法是参数保持一致;

 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> 鸭子类型:不管是什么类对象,只要会叫,就认为是鸭子在叫,这种就叫鸭子类型

 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()实现反射,而其他方法则属于自省

 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,因为上面删除了该属性
动态操作属性
原文地址:https://www.cnblogs.com/quiet-sun/p/14340709.html