17-Python-面向对象

1、面向对象基本概念

OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

2、面向对象核心特性

2.1 Class 类

一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法 。

2.2 Object 对象

一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性(obj1 = class(),obj2 = class(),...)。就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同。

2.3 Encapsulation 封装

在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法。

2.4 Inheritance 继承

一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。

2.5 Polymorphism 多态

多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。

3、定义类

3.1 类的基本定义方法

1 class Dog(object):
2     print("I am a dog.")
3 
4 
5 dog1 = Dog()  # 实例化这个类,此时的dog1就是类Dog的实例化对象。

 

class Dog(object):
    def sayhi(self):
        print("I am a dog.")


dog = Dog()  # 实例化这个类,此时的dog就是类Dog的实例化对象。
dog1 = dog.sayhi() 

 

3.2 类的实例化

实例化,其实就是以Dog类为模版,在内存里开辟一块空间,存上数据,赋值成一个变量名。

 1 class Dog(object):
 2  
 3     def __init__(self,name,dog_type):  # 构造方法
 4         self.name = name  # 属性/成员变量
 5         self.type = dog_type
 6  
 7     def sayhi(self):  # 方法/动态属性
 8  
 9         print("hello,I am a dog, my name is ",self.name)
10  
11  
12 d = Dog('LiChuang',"京巴")  # Dog(d,"LiChuang","京巴") ---> 
13 # def __init__(self,name,dog_type) ---> def __init__(d,"LiChuang","京巴")
14 # d.name = "LiChuang"
15 # d.type = "京巴"
16 
17 d.sayhi()  # print("hello,I am a dog, my name is ",d.name) --->
18 # print("hello,I am a dog, my name is ","LiChuang")

 

 根据上图我们得知,其实self,就是实例本身!你实例化时python会自动把这个实例本身通过self参数传进去。

下面这个示例的道理一样:

 1 class Dog(object):
 2     def __init__(self, name):
 3         self.NAME = name
 4 
 5     def sayhi(self):
 6         print("I am a dog. My name is", self.NAME)
 7 
 8     def eat(self, food):
 9         print("[%s] is eating [%s]." % (self.NAME, food))
10 
11 
12 dog = Dog("zhangsan")  # Dog(dog, "zhangsan") ---> def __init__(dog,"zhangsan") ---> dog.NAME = "zhangsan"
13 dog1 = Dog("lisi")  # Dog(dog, "lisi") ---> def __init__(dog,"lisi") ---> dog.NAME = "lisi"
14 
15 dog.sayhi() # Role.sayhi(dog) ---> print("I am a dog. My name is zhangsan")
16 dog1.sayhi()
17 
18 dog.eat("bone1")  # Role.eat(dog,"bone1") ---> print(" [zhangsan] is eating [bone1].")
19 dog1.eat("bone2")

 

一个稍微复杂点的类:

 1 class Role(object):
 2     def __init__(self, name, role, weapon, life_value=100, money=15000): # 初始化函数,在生成一个角色时要初始化的一些属性就填写在这里
 3         self.name = name
 4         self.role = role
 5         self.weapon = weapon
 6         self.life_value = life_value
 7         self.money = money
 8 
 9     def shot(self):
10         print("%s is shooting..." % self.name)
11 
12     def got_shot(self, shoter):
13         print("%s had got_shot by [\033[31;1m%s\033[0m]" % (self.name, shoter))
14 
15     def buy_gun(self):
16         print("%s bought a [%s]" % (self.name, self.weapon))
17     #  如果要重新传一个枪的值,用下面的方法
18     #  def buy_gun(self, gun_name):
19     #  print("%s bought a [%s]" % (self.name, gun_name))
20     #    # self.weapon = gun_name  # 修改类对象的属性
21     
22 
23 
24 role1 = Role("zhangsan", "police", "M16")  # Role(role1, "zhangsan", "police", "M16") ------->
25 # def __init__(role1,"zhangsan", "police", "M16", life_value=100, money=15000) ------->
26 # role1.name = "zhangsan"
27 # role1.role = "police
28 # role1.weapon = "M16"
29 # role1.life_value = 100
30 # role1.money = 15000
31 
32 role1.shot()  # shot(role1) --> print("%s is shooting..." % role1.name) --> print("%s is shooting..." % "zhangsan")
33 
34 print("华丽的分隔符".center(50, "-"))
35 
36 role2 = Role("lisi", "police", "AUG")
37 role2.got_shot("druid")
38 role2.buy_gun()
39 
40 #  如果要重新传递一个枪的值,用下面的方法
41 #  role2.buy_gun("M4")
42 #  print(role2.weapon)  # 结果仍然为AUG,修改方法:在函数中增加 self.weapon = gun_name

上面的这个__init__()叫做初始化方法(或构造方法), 在类被调用时,这个方法(虽然它是函数形式,但在类中就不叫函数了,叫方法)会自动执行,进行一些初始化的动作,所以我们这里写的__init__(self,name,role,weapon,life_value=100,money=15000)就是要在创建一个角色时给它设置这些属性,那么这第一个参数self是干毛用的呢? self代表执行当前方法的对象。

  初始化一个角色,就需要调用这个类一次:
1 role1 = Role("zhangsan", "police", "M16")   # 生成一个角色 , 会自动把参数传给Role下面的__init__(...)方法
2 role2 = Role("lisi", "police", "AUG")

我们看到,上面的创建角色时,我们并没有给__init__传值,程序也没未报错,是因为,类在调用它自己的__init__(…)时自己帮你给self参数赋值了:

1 role1 = Role("zhangsan", "police", "M16")  # 此时self相当于role1 ---> Role(role1, "zhangsan", "police", "M16")
2 role2 = Role("lisi", "police", "AUG")  # 此时self相当于role2 ---> Role(role2, "lisi", "police", "AUG")

你执行role1 = Role("zhangsan", "police", "M16")时,python的解释器其实干了两件事:

  1. 在内存中开辟一块空间指向r1这个变量名

  2. 调用Role这个类并执行其中的__init__(…)方法,相当于Role.__init__(role1, "zhangsan", "police", "M16"),这么做是为什么呢? 是为了把"zhangsan", "police", "M16"这3个值跟刚开辟的role1关联起来,是为了把"zhangsan", "police", "M16"这3个值跟刚开辟的role1关联起来,是为了把"zhangsan", "police", "M16"这3个值跟刚开辟的role1关联起来,重要的事情说3次, 因为关联起来后,你就可以直接role1.name, role1.weapon 这样来调用啦。所以,为实现这种关联,在调用__init__方法时,就必须把role1这个变量也传进去,否则__init__不知道要把那3个参数跟谁关联呀。

  3. 明白了么哥?所以这个__init__(…)方法里的,self.name = name , self.role = role 等等的意思就是要把这几个值存到role1的内存空间里。

但又问, __init__(…)我懂了,但后面的那几个函数,噢 不对,后面那几个方法为什么也还需要self参数么? 不是在初始化角色的时候 ,就已经把角色的属性跟role1绑定好了么?先来看一下上面类中的一个buy_gun的方法: 

1 def buy_gun(self, gun_name):
2         print("%s bought a [%s]" % (self.name, gun_name))

上面这个方法通过类调用的话要写成如下: 

1 role1 = Role("zhangsan", "police", "M16")
2 role1.buy_gun("M4")  # python会自动帮你转成 Role.buy_gun(role1,”M4")

执行结果为:zhangsan bought a [M4]

依然没给self传值 ,但Python还是会自动的帮你把role1赋值给self这个参数,为什么呢? 因为,你在buy_gun(..)方法中可能要访问role1的一些其它属性呀, 比如这里就访问了role1的名字,怎么访问呢?你得告诉这个方法呀,于是就把role1传给了这个self参数,然后在buy_gun里调用 self.name 就相当于调用role1.name 啦,如果还想知道role1的生命值 有多少,直接写成self.life_value就可以了。 说白了就是在调用类中的一个方法时,你得告诉人家你是谁。

好啦, 总结一下2点:
  1. 上面的这个role1 = Role("zhangsan", "police", "M16")动作,叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了, 这个对象就叫做实例。
  2. 刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容。

 对封装的深入理解:

 1 class A(object):
 2     def __init__(self, name):
 3         self.N = name
 4         print(name)
 5 
 6 
 7 class B(object):
 8     def __init__(self, arg1):
 9         self.a = arg1
10 
11 
12 class C(object):
13     def __init__(self, arg2):
14         self.b = arg2
15 
16 
17 a = A("Druid")  # A(a,"Druid") ---> a.N = "Druid"
18 b = B(a)  # B(b,a) ---> b.a = a
19 c = C(b)  # C(c,b) ---> c.b = b
20 
21 # 要求只是用对象c来输出"Druid",应该怎么做?
22 c.b.a.N  # 使用c打印出"Druid"
23 # 解析如下:
24 # 由上述三式可得:a.N = "Druid" ---> b.a.N = "Druid" ---> c.b.a.N = "Druid"

 

3.3 类的继承和重构

3.3.1 单继承和重构

先看一个简单的继承:

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5         self.sex = "normal"
 6 
 7     def talk(self):
 8         print("person is talking....")
 9 
10 
11 class WhitePerson(Person):
12     def swim(self):
13         print("[%s]blackperson is swimming....he is [%s] old" % (self.name, self.age))
14 
15 
16 class BlackPerson(Person):
17     def walk(self):
18         print("[%s]blackperson is walking....he is [%s] old" % (self.name, self.age))
19 
20 
21 w = WhitePerson("John", "25")
22 w.swim()  # 调用自己类的方法
23 w.talk()  # 直接继承父类(基类)的方法
24 
25 b = BlackPerson("smith", "30")
26 b.walk()  # 调用自己类的方法
27 b.talk()  # 直接继承父类(基类)的方法

 

接着来一个稍微复杂一点的继承+重构:

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5         self.sex = "normal"
 6 
 7     def talk(self):
 8         print("person is talking....")
 9 
10 
11 class WhitePerson(Person):
12     def swim(self):
13         print("[%s]whiteperson is swimming....he is [%s] old" % (self.name, self.age))
14 
15 
16 class BlackPerson(Person):
17     def walk(self):
18         print("[%s]blackperson is walking....he is [%s] old" % (self.name, self.age))
19 
20     def talk(self):
21         print("[%s]blackperson is talking English,he is [%s] old" % (self.name, self.age))
22 
23 
24 w = WhitePerson("John", "25")
25 w.swim()  # 调用自己类的方法
26 w.talk()  # 直接继承父类(基类)的方法
27 
28 print("华丽的分隔符".center(50, "-"))
29 
30 b = BlackPerson("smith", "30")
31 b.walk()  # 调用自己类的方法
32 b.talk()  # 重构talk方法

 

再看一个更复杂的:

 1 class Person(object):
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5         self.sex = "normal"
 6 
 7     def talk(self):
 8         print("person is talking....")
 9 
10 
11 class WhitePerson(Person):
12     def swim(self):
13         print("[%s]whiteperson is swimming....he is [%s] old" % (self.name, self.age))
14 
15 
16 class BlackPerson(Person):
17     def __init__(self, name, age, strength):
18         self.strength = strength
19         Person.__init__(self, name, age)
20         print(self.name, self.age, self.sex)
21 
22     def walk(self):
23         print("[%s]blackperson is walking so fast,cuz his power is [%s]" % (self.name, self.strength))
24 
25     def talk(self):
26         print("[%s]blackperson is talking English,he is [%s] old" % (self.name, self.age))
27 
28 
29 w = WhitePerson("John", "25")
30 w.swim()  # 调用自己类的方法
31 w.talk()  # 直接继承父类(基类)的方法
32 
33 print("华丽的分隔符".center(50, "-"))
34 
35 b = BlackPerson("smith", "30", "strong")  # BlackPerson(b,"smith", "30", "strong")
36 b.walk()  # 调用自己类的方法
37 b.talk()  # 重构talk方法

对于代码的解释请看下图:

 

 更加复杂的继承+重构代码示例:

 1 class SchoolMember(object):
 2     """学校成员基类"""
 3     member = 0
 4 
 5     def __init__(self, name, age, sex):
 6         self.name = name
 7         self.age = age
 8         self.sex = sex
 9         self.enroll()
10 
11     def enroll(self):
12         print("新注册的成员名字[%s]" % self.name)
13         SchoolMember.member += 1  # 每调用一次方法则加一
14 
15     def tell(self):
16         print("----------[%s]信息如下----------" % self.name)
17         for k, v in self.__dict__.items():  # __dict__将数据转换为字典的形式
18             print("\t", k, v)
19         print("--------------End--------------")
20 
21     def __del__(self):  # 析构
22         print("学生[%s]被开除了!" % self.name)
23         SchoolMember.member -= 1
24 
25 
26 class Teacher(SchoolMember):
27     """讲师子类"""
28     def __init__(self, name, age, sex, course, salary):
29         SchoolMember.__init__(self, name, age, sex)  # 经典类写法
30         # super(Teacher, self).__init__(name, age, sex)  # 新式类写法,与上面作用一模一样
31         self.course = course
32         self.salary = salary
33 
34     def teaching(self):
35         print("[%s]老师教[%s]课" % (self.name, self.course))
36 
37     def payroll(self):
38         print("[%s]老师的工资是[%s]元" % (self.name, self.salary))
39 
40 
41 class Student(SchoolMember):
42     """学生子类"""
43     def __init__(self, name, age, sex, course, tuition):
44         SchoolMember.__init__(self, name, age, sex)
45         self.course = course
46         self.tuition = tuition
47 
48     def stu_cource(self):
49         print("[%s]的选学的课程是[%s]" % (self.name, self.course))
50 
51     def pay_tuition(self):
52         print("[%s]交了[%s]元学费" % (self.name, self.tuition))
53 
54 
55 t1 = Teacher("Alex", 38, "Male", "Python", 100000)  # Teacher(t1,"Alex",38,"Male","Python",100000)
56 s1 = Student("Druid", 25, "Male", "Python", 6888)  # Student(s1,"Druid",25,"Male","Python",6888)
57 s2 = Student("Zhangsan", 26, "Male", "java", 9888)  # Student(s2,"Zhangsan",26,"Male","Python",6888)
58 
59 print("一共注册了[%s]位成员" % SchoolMember.member)
60 
61 t1.tell()
62 t1.teaching()
63 t1.payroll()
64 
65 print("华丽的分隔符".center(50, "-"))
66 
67 s1.tell()
68 s1.stu_cource()
69 s1.pay_tuition()
70 
71 print("华丽的分隔符".center(50, "-"))
72 
73 s2.tell()
74 s2.stu_cource()
75 s2.pay_tuition()
76 
77 print("华丽的分隔符".center(50, "-"))
78 
79 del s2
80 print(SchoolMember.member)

 

3.3.2 多继承和重构
 1 class SchoolAddress(object):
 2     """学校地址基类"""
 3     def __init__(self, address):
 4         self.address = address
 5 
 6     def info(self):
 7         print("学校所在的位置[%s]" % self.address)
 8 
 9 
10 class SchoolMember(object):
11     """学校成员基类"""
12     member = 0
13 
14     def __init__(self, name, age, sex):
15         self.name = name
16         self.age = age
17         self.sex = sex
18         self.enroll()
19 
20     def enroll(self):
21         print("新注册的成员名字[%s]" % self.name)
22         SchoolMember.member += 1  # 每调用一次方法则加一
23 
24     def tell(self):
25         print("----------[%s]信息如下----------" % self.name)
26         for k, v in self.__dict__.items():  # __dict__将数据转换为字典的形式
27             print("\t", k, v)
28         print("--------------End--------------")
29 
30     def __del__(self):  # 析构
31         print("学生[%s]被开除了!" % self.name)
32         SchoolMember.member -= 1
33 
34 
35 class Teacher(SchoolAddress, SchoolMember):  # 多继承
36     """讲师子类"""
37     def __init__(self, name, age, sex, address, course, salary):
38         SchoolMember.__init__(self, name, age, sex)  # 经典类写法
39         SchoolAddress.__init__(self, address)
40         # super(Teacher, self).__init__(name, age, sex)  # 新式类写法,与上面作用一模一样
41         self.course = course
42         self.salary = salary
43 
44     def teaching(self):
45         print("[%s]老师教[%s]课" % (self.name, self.course))
46 
47     def payroll(self):
48         print("[%s]老师的工资是[%s]元" % (self.name, self.salary))
49 
50 
51 class Student(SchoolAddress, SchoolMember):  # 多继承
52     """学生子类"""
53     def __init__(self, name, age, sex, address, course, tuition):
54         SchoolMember.__init__(self, name, age, sex)
55         SchoolAddress.__init__(self, address)
56         self.course = course
57         self.tuition = tuition
58 
59     def stu_cource(self):
60         print("[%s]的选学的课程是[%s]" % (self.name, self.course))
61 
62     def pay_tuition(self):
63         print("[%s]交了[%s]元学费" % (self.name, self.tuition))
64 
65 
66 t1 = Teacher("Alex", 38, "Male", "成都", "Python", 100000)  # Teacher(t1,"Alex",38,"Male","成都","Python",100000)
67 s1 = Student("Druid", 25, "Male", "北京", "Python", 6888)  # Student(s1,"Druid",25,"Male","北京","Python",6888)
68 s2 = Student("Zhangsan", 26, "Male", "北京", "java", 9888)  # Student(s2,"Zhangsan",26,"Male","北京","Python",6888)
69 
70 print("一共注册了[%s]位成员" % SchoolMember.member)
71 
72 t1.tell()
73 t1.teaching()
74 t1.payroll()
75 t1.info()  # Teacher.info(t1) -->
76 
77 print("华丽的分隔符".center(50, "-"))
78 
79 s1.tell()
80 s1.stu_cource()
81 s1.pay_tuition()
82 s1.info()
83 
84 print("华丽的分隔符".center(50, "-"))
85 
86 s2.tell()
87 s2.stu_cource()
88 s2.pay_tuition()
89 s2.info()
90 
91 print("华丽的分隔符".center(50, "-"))
92 
93 del s2
94 print(SchoolMember.member)

 

3.3.3 对继承的深入理解

 1 class F1(object):
 2     def __init__(self):
 3         print("F1")
 4 
 5     def a1(self):
 6         print("F1a1")
 7 
 8     def a2(self):
 9         print("F1a2")
10 
11 
12 class F2(F1):
13     def __init__(self):
14         print("F2")
15 
16     def a1(self):
17         self.a2()
18         print("F2a1")
19 
20     def a2(self):
21         print("F2a2")
22 
23 
24 class F3(F2):
25     def __init__(self):
26         print("F3")
27 
28     def a2(self):
29         print("F3a2")
30 
31 
32 obj = F3()
33 obj.a1()
34 #>>>  F3
35       F3a2
36       F2a1    

代码图解如下:

 

3.4 析构函数

析构函数的定义:Python的垃圾回收机制回收内存后,析构函数才会被调用。下面的示例中,没有调用函数__del__,但结果还是打印了del....run.... 。 

 1 class Role(object):
 2     nationality = "CN"  # 公有属性。直接在类里面定义的属性为公有属性。
 3 
 4     def __init__(self, name, role, weapon, life_value=100, money=15000):
 5         self.name = name
 6         self.role = role
 7         self.weapon = weapon
 8         self.life_value = life_value
 9         self.money = money
10         self.__heart = "normal"  # __开头的属性/成员变量为私有属性,不能从外部调用
11 
12     def shot(self):
13         print("%s is shooting..." % self.name)
14 
15     def got_shot(self, shoter):
16         print("%s had got_shot by [\033[31;1m%s\033[0m]" % (self.name, shoter))
17         self.__heart = "Die"
18         print(self.__heart)
19 
20     def got_heart(self):  # 可以使用该方法调用私有属性(只读不能改)
21         return self.__heart
22 
23     def buy_gun(self, gun_name):
24         print("%s bought a [%s]" % (self.name, gun_name))
25         # self.weapon = gun_name  # 修改类对象的属性
26 
27     def __del__(self):  # 析构函数。用于程序的收尾工作。
28         print("del....run....")
29 
30 
31 role1 = Role("zhangsan", "police", "M16")  # Role(role1, "zhangsan", "police", "M16") ------->
32 # def __init__(role1,"zhangsan", "police", "M16", life_value=100, money=15000) ------->
33 # role1.name = "zhangsan"
34 # role1.role = "police
35 # role1.weapon = "M16"
36 # role1.life_value = 100
37 # srole1.money = 15000

 

3.5 新式类和经典类

 1 class Person:
 2     """
 3     经典类写法
 4     """
 5     def __init__(self, name, age):
 6         self.name = name
 7         self.age = age
 8         print("[%s]写法,[%s]年" % (self.name, self.age))
 9 
10 
11 class NewPerson(object):
12     """
13     新式类写法。其中的object为基类。
14     """
15     def __init__(self, name, age):
16         self.name = name
17         self.age = age
18         print("[%s]写法,[%s]年" % (self.name, self.age))
19 
20 
21 class Old(Person):
22     """
23     经典类继承,Old类继承Person类
24     """
25     def __init__(self, name, age, salary=999):
26         Person.__init__(self, name, age)  # 经典类的继承写法
27         self.salary = salary
28 
29 
30 class New(NewPerson):
31     """
32      新式类继承,New类继承New_Person类
33     """
34     def __init__(self, name, age, salary=111):
35         super(New, self).__init__(name, age)  # 新式类的继承写法
36         self.salary = salary
37 
38 
39 oldperson = Person("经典类", "999")
40 print(oldperson.name)
41 print(oldperson.age)
42 
43 print("分隔符".center(50, "-"))
44 
45 newperson = NewPerson("新式类", "111")
46 
47 print(newperson.name)
48 print(newperson.age)
49 
50 print("分隔符".center(50, "-"))
51 
52 old = Old("经典类继承", "99")
53 print(old.name)
54 print(old.age)
55 print(old.salary)
56 
57 print("分隔符".center(50, "-"))
58 
59 new = New("新式类继承", "11")
60 print(new.name)
61 print(new.age)
62 print(new.salary)
63 
64 
65 """
66 输出结果:
67 [经典类]写法,[999]年
68 经典类
69 999
70 -----------------------分隔符------------------------
71 [新式类]写法,[111]年
72 新式类
73 111
74 -----------------------分隔符------------------------
75 [经典类继承]写法,[99]年
76 经典类继承
77 99
78 999
79 -----------------------分隔符------------------------
80 [新式类继承]写法,[11]年
81 新式类继承
82 11
83 111
84 """

 

3.6 Python2、Python3新式类、经典类多继承之广度查询和深度查询

在Python2中,对于新式类和经典类在多继承时,有光度查询和深度查询这么一说。当然,Python3中不存在该问题了。如下例:

 1 class A(object):  # 新式类
 2     pass
 3     # def __init__(self):
 4     #     self.n = "A"
 5 
 6 
 7 class A:  # 经典类
 8     pass
 9     # def __init__(self):
10     #     self.n = "A"
11 
12 
13 class B(A):
14     pass
15     # def __init__(self):
16     #     self.n = "B"
17 
18 
19 class C(A):
20     pass
21     # def __init__(self):
22     #     self.n = "C"
23 
24 
25 class D(B, C):  # 多继承
26     pass
27     # def __init__(self):
28     #     self.n = "D"
29 
30 
31 d = D()
32 print(d.n)
  1. 在Python2中,对于新式类,如果将D注释掉,那么将打印"B";在此基础上,将B注释掉,那么将打印"C":继续将C注释掉,那么将打印"A"。此为广度查询
  2. 在Python2中,对于经典类,如果将D注释掉,那么将打印"B":在此基础上,将B注释掉,那么将打印"A":继续将A注释掉,那么将打印"C"。此为深度查询

广度查询图解如下:

深度查询图解如下:

 3.7 静态方法、类方法和属性方法

3.7.1 静态方法

通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法。

 1 class Dog(object):
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     @staticmethod  # 实际上根类没什么关系了,
 6     def eat(self):
 7         print("[%s]吃[%s]" % (self.name, "包子"))
 8 
 9 
10 dog1 = Dog("alex")
11 dog1.eat()

上面的调用会出以下错误,说是eat需要一个self参数,但调用时却没有传递,没错,当eat变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。

Traceback (most recent call last):
  File "E:/Python/Python/06-Day6/01-Practise/staticmethod.py", line 14, in <module>
    dog1.eat()
TypeError: eat() missing 1 required positional argument: 'self'

想让上面的代码可以正常工作有两种办法:

1. 调用时主动传递实例本身给eat方法,即dog1.eat(dog1) ;

 1 class Dog(object):
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     @staticmethod  # 实际上根类没什么关系了,
 6     def eat(self):
 7         print("[%s]吃[%s]" % (self.name, "包子"))
 8 
 9 
10 dog1 = Dog("alex")
11 dog1.eat(dog1)

 

2. 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了。

 1 class Dog(object):
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     @staticmethod  # 实际上根类没什么关系了,
 6     def eat():
 7         print("吃[%s]" % "包子")
 8 
 9 
10 dog1 = Dog("alex")
11 dog1.eat()

 

3.7.2 类方法

类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量(公有属性),不能访问实例变量(属性)。

 1 class Dog(object):
 2     def __init__(self,name):
 3         self.name = name
 4  
 5     @classmethod
 6     def eat(self):
 7         print("%s is eating" % self.name)
 8  
 9  
10  
11 dog1 = Dog("alex")
12 dog1.eat()

执行报错如下,说Dog没有name属性,因为name是个实例变量(属性),类方法是不能访问实例变量(属性)的。

Traceback (most recent call last):
  File "E:/Python/Python/06-Day6/01-Practise/class method.py", line 15, in <module>
    dog1.eat()
  File "E:/Python/Python/06-Day6/01-Practise/class method.py", line 10, in eat
    print("%s is eating" % self.name)
AttributeError: type object 'Dog' has no attribute 'name'

此时可以定义一个类变量,也叫name,看下执行效果:

 1 class Dog(object):
 2 
 3     name = "类变量名字"
 4 
 5     def __init__(self, name):
 6         self.name = name
 7 
 8     @classmethod  # 类方法只能访问类变量(即公有属性)
 9     def eat(self):
10         print("[%s]吃[%s]" % (self.name, "包子"))
11 
12 
13 dog1 = Dog("alex")  # 该属性/成员变量没有被使用
14 dog1.eat()  # 打印结果:[类变量名字]吃[包子]

 

3.7.3 属性方法

属性方法的作用就是通过@property把一个方法变成一个静态属性。

 1 class Dog(object): 
 2     def __init__(self,name):
 3         self.name = name
 4  
 5     @property
 6     def eat(self):
 7         print(" %s is eating" % self.name)
 8  
 9  
10 dog1 = Dog("alex")
11 dog1.eat()  # 属性不能通过加()调用

调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 不是方法了, 想调用已经不需要加()号了,直接dog1.eat就可以了。

Traceback (most recent call last):
  File "E:/Python/Python/06-Day6/01-Practise/propertymethod.py", line 15, in <module>
    dog1.eat()  # 属性不能通过加()调用
TypeError: 'NoneType' object is not callable

正常调用如下:

 1 class Dog(object):
 2 
 3     def __init__(self, name):
 4         self.name = name
 5 
 6     @property  # 把方法变为一个静态属性。不能再接收参数。
 7     def eat(self):
 8         print("[%s]吃[%s]" % (self.name, "包子"))
 9 
10 
11 dog1 = Dog("alex")  
12 dog1.eat  # 输出:[alex]吃[包子]

 

3.8 类的特殊方法

3.8.1 __doc__表示类的描述信息

1 class Class(object):
2     """ 描述类信息,这是用于看片的神奇 """
3 
4     def func(self):
5         pass
6 
7 
8 print(Class.__doc__)  # 输出结果:描述类信息,这是用于看片的神奇 

3.8.2  __module__ 和  __class__ 

1 class C(object):
2 
3     def __init__(self):
4         self.name = 'wupeiqi'
1 from lib.aa import C
2 
3 obj = C()
4 print(obj.__module__)  # 输出 lib.aa,即:输出模块
5 print(obj.__class__)      # 输出 lib.aa.C,即:输出类

3.8.3 __init__ 构造方法,通过类创建对象时,自动触发执行。

3.8.4 __del__ 析构方法

析构方法,当对象在内存中被释放时,自动触发执行。

3.8.5 __call__ 对象后面加括号,触发执行

构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()。

 1 class Class(object):
 2     def __init__(self):
 3         print("__init__")
 4 
 5     def __call__(self, *args, **kwargs):
 6         print("__call__")
 7 
 8 
 9 obj = Class()  # 执行 __init__,打印:__init__
10 obj()  # 执行 __call__,打印:__call__

3.8.6 __dict__ 查看类或对象中的所有成员 

 1 class Province(object):
 2     
 3     country = "China"
 4 
 5     def __init__(self, name, count):
 6         self.name = name
 7         self.count = count
 8 
 9     def func(self, *args, **kwargs):
10         print("func")
11 
12 
13 # 获取类的成员,即:属性、方法
14 print(Province.__dict__)
15 # 输出:{'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x0000019C7076CF28>, 'func': <function Province.func at 0x0000019C70771048>, '__dict__': <attribute '__dict__' of 'Province' objects>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}
16 
17 obj1 = Province("HeBei", 10000)
18 print(obj1.__dict__)
19 # 获取 对象obj1 的成员
20 # 输出:{'name': 'HeBei', 'count': 10000}
21 
22 obj2 = Province("HeNan", 3888)
23 print(obj2.__dict__)
24 # 获取 对象obj2 的成员
25 # 输出:{'name': 'HeNan', 'count': 3888}

3.8.7 __str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。

1 class Class(object):
2     def __str__(self):
3         return "alex li"
4 
5 
6 obj = Class()
7 print(obj)  # 输出:alex li

3.8.8 __getitem__、__setitem__、__delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据。

 1 class Class(object):
 2     def __getitem__(self, key):
 3         print("__getitem__", key)
 4 
 5     def __setitem__(self, key, value):
 6         print("__setitem__", key, value)
 7 
 8     def __delitem__(self, key):
 9         print("__delitem__", key)
10 
11 
12 obj = Class()
13 
14 result = obj["k1"]  # 自动触发执行 __getitem__ 。打印:__getitem__ k1
15 obj["k2"] = "alex"  # 自动触发执行 __setitem__ 。打印:__setitem__ k2 alex
16 del obj["k1"]  # 打印:__delitem__ k1

3.8.9 __new__ \ __metaclass__

1 class Class(object):
2  
3  
4     def __init__(self,name):
5         self.name = name
6  
7  
8 f = Class("alex")

上述代码中,obj 是通过 Class 类实例化的对象,其实,不仅 obj 是一个对象,Class类本身也是一个对象,因为在Python中一切事物都是对象

如果按照一切事物都是对象的理论:obj对象是通过执行Class类的构造方法创建,那么Class类对象应该也是通过执行某个类的 构造方法 创建。

print type(f) # 输出:<class '__main__.Class'>     表示,obj 对象由Class类创建
print type(Class) # 输出:<type 'type'>              表示,Class类对象由 type 类创建

所以,f对象是Class类的一个实例,Class类对象是 type 类的一个实例,即:Class类对象 是通过type类的构造方法创建。

那么,创建类就可以有两种方式:

a). 普通方式 

class Class(object):
  
    def func(self):
        print(hello alex)

b). 特殊方式

1 def func(self):
2     print(hello wupeiqi)
3   
4 Class = type('Class',(object,), {'func': func})
5 #type第一个参数:类名
6 #type第二个参数:当前类的基类
7 #type第三个参数:类的成员

加上构造方法:

 1 def func(self):
 2     print("hello %s" %self.name)
 3 
 4 def __init__(self,name,age):
 5     self.name = name
 6     self.age = age
 7 Class = type('Class',(object,),{'func':func,'__init__':__init__})
 8 
 9 f = Class("jack",22)
10 f.func()  

 

 

So ,孩子记住,类 是由 type 类实例化产生。

那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?

答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。

 

复制代码
 1 class MyType(type):
 2     def __init__(self,*args,**kwargs):
 3 
 4         print("Mytype __init__",*args,**kwargs)
 5 
 6     def __call__(self, *args, **kwargs):
 7         print("Mytype __call__", *args, **kwargs)
 8         obj = self.__new__(self)
 9         print("obj ",obj,*args, **kwargs)
10         print(self)
11         self.__init__(obj,*args, **kwargs)
12         return obj
13 
14     def __new__(cls, *args, **kwargs):
15         print("Mytype __new__",*args,**kwargs)
16         return type.__new__(cls, *args, **kwargs)
17 
18 print('here...')
19 class Foo(object,metaclass=MyType):
20 
21 
22     def __init__(self,name):
23         self.name = name
24 
25         print("Foo __init__")
26 
27     def __new__(cls, *args, **kwargs):
28         print("Foo __new__",cls, *args, **kwargs)
29         return object.__new__(cls)
30 
31 f = Foo("Alex")
32 print("f",f)
33 print("fname",f.name)
复制代码
自定义元类

 

 类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__

 metaclass 详解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高那个答案写的非常好

 

3.9 反射

3.9.1 hasattr和getattr

hasattr(obj,name_str)判断obj对象中是否存在对应的字符串的方法。getaddr(obj,name_str)根据字符串去获取obj对象里的对应的方法或属性的内存地址。

 1 class Dog(object):
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     def eat(self, food):
 6         print("%s正在吃%s" % (self.name, food))
 7 
 8     def talk(self, voice):
 9         print("{}在叫:{}".format(self.name, voice))
10 
11 
12 dog1 = Dog("alex")
13 choice = input(">>:").strip()
14 
15 print(hasattr(dog1, choice))  # hasattr(obj,name_str)判断obj对象中是否存在对应的字符串的方法。存在返回True,否则返回False。
16 
17 if hasattr(dog1, choice):
18     if choice == "eat":
19         getattr(dog1, choice)("包子")   # 输出:alex正在吃包子
20     elif choice == "talk":
21         getattr(dog1, choice)("汪汪汪")  # 输出:alex在叫:汪汪汪
22 else:
23     print("不存在对应的方法或对象!")

3.9.2 setattr

 1 class Dog(object):
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     def eat(self, food):
 6         print("%s正在吃%s" % (self.name, food))
 7 
 8 
 9 def talk(self):
10     print("%s在叫" % self.name)
11 
12 
13 dog1 = Dog("alex")
14 choice = input(">>:").strip()
15 
16 print(hasattr(dog1, choice))  # hasattr(obj,name_str)判断obj对象中是否存在对应的字符串的方法
17 
18 if hasattr(dog1, choice):
19     getattr(dog1, choice)("包子")  # getaddr(obj,name_str)根据字符串去获取obj对象里的对应的方法或属性的内存地址
20 else:
21     setattr(dog1, choice, talk)  # 动态增加方法.setattr(obj,"y",v)相当于obj.y = v
22     dog1.talk(dog1)  # 输入talk,输出:alex在叫
23 
24     setattr(dog1, choice, 22)  # 动态增加属性
25     print(getattr(dog1, choice))  # 输出:22

 

3.9.3 delattr

class Dog(object):
    def __init__(self, name):
        self.name = name

    def eat(self, food):
        print("%s正在吃%s" % (self.name, food))


def talk(self):
    print("%s在叫" % self.name)


dog1 = Dog("alex")
choice = input(">>:").strip()

print(hasattr(dog1, choice))  # hasattr(obj,name_str)判断obj对象中是否存在对应的字符串的方法

if hasattr(dog1, choice):
    getattr(dog1, choice)("包子")  # getaddr(obj,name_str)根据字符串去获取obj对象里的对应的方法或属性的内存地址
else:
    setattr(dog1, choice, talk)  # 动态增加方法.setattr(obj,"y",v)相当于obj.y = v
    dog1.talk(dog1)

    # setattr(dog1, choice, 22)  # 动态增加属性
    # print(getattr(dog1, choice))

>>:name  # 输入name
print(dog1.name)  # 输出:alex
delattr(dog1, choice)  # delattr(obj,属性)删除obj对象中对应的属性
print(dog1.name)  # 报错,因为已经删除了name属性

 

转载于:https://www.cnblogs.com/Druidchen/p/8121090.html

### 回答1: 魔术方法是Python中的特殊方法,它们以双下划线开头和结尾,例如__init__、__str__、__add__等。这些方法可以在类的实例化、运算符重载、属性访问等方面提供特殊的行为。 __init__方法是一个特殊的构造函数,用于初始化类的实例。__str__方法用于返回对象的字符串表示形式,可以通过print函数输出。__add__方法用于重载加法运算符,可以实现自定义的加法操作。其他常用的魔术方法还包括__eq__、__lt__、__gt__等,用于比较运算符的重载。 学习魔术方法可以让我们更好地理解Python面向对象编程的特性,提高代码的可读性和可维护性。 ### 回答2: 魔术方法是Python中最有趣且也是最强大的概念之一。魔术方法(也称为特殊方法或双下划线方法)是一些特殊的方法,它们以双下划线(__)开头和结尾,并具有特定的名称。 这些特殊方法可以为我们提供许多有用的功能,例如重载操作符,处理类的属性,实现自定义迭代器,使用描述符等。 下面是一些常见的魔术方法: __init__:这是最常见的魔术方法。当创建一个实例时,它会被自动调用。它用于初始化对象的属性。 __str__:当你想要将一个对象转换成字符串时,这个方法会被调用。如果你不指定__str__方法,Python默认会使用对象的类名和内存地址来表示对象。 __repr__:这个方法和__str__方法类似,也是用于将对象转换成字符串。但是__repr__方法在调试时有很大的作用,因为它返回的字符串可以用来唯一地标识对象。 __len__:这个方法可以返回对象的长度。例如,如果你想获取一个字符串的长度,你可以使用len("hello"),在底层,它实际上是调用了字符串对象的__len__方法。 __getattr__和__setattr__:这些方法允许你动态地获取和设置对象的属性。当你访问一个不存在的属性时,__getattr__方法会被调用。当你设置一个属性时,__setattr__方法会被调用。 __call__:这个方法允许你将对象作为函数调用。当你调用一个对象时,Python实际上是在调用对象的__call__方法。 除了上面列举的方法,还有许多其他的魔术方法,例如__cmp__,__hash__,__iter__等等。学习这些魔术方法将使你能够更好地理解Python面向对象编程模型。 总之,学习和理解魔术方法是Python面向对象编程中的一个关键概念,因为它们可以帮助你实现更加灵活和强大的代码。如果你想成为一名Python高手,那么深入学习魔术方法是不可避免的。 ### 回答3: Python中的“魔术方法”指的是每个类中定义的特殊方法,它们以双下划线(__)开头和结尾,并且有着特定的用途。通过使用这些魔法方法,我们可以自定义类的行为,并为程序提供更高级别的功能。 以下Python中常用的一些魔术方法: 1. __init__:这是最常用的魔术方法之一,它用于初始化一个类的对象,以及定义类的属性和方法。 2. __str__:此方法用于返回对象的字符串表示形式,类似于Java中的toString()方法。 3. __repr__:与__str__类似,但是返回的是对象的“官方”字符串表示形式,通常用于调试和开发。 4. __getattr__:当试图访问一个不存在的属性时,此方法被调用。 5. __setattr__:当尝试设置类的属性时,此方法被调用。 6. __delattr__:当尝试删除类的属性时,此方法被调用。 7. __call__:将对象作为函数调用时,此方法被调用。 8. __len__:返回对象的长度。 9. __getitem__:允许通过索引访问对象的元素。 10. __setitem__:允许通过索引设置对象的元素。 11. __delitem__:允许通过索引删除对象的元素。 通过了解和使用这些魔术方法,我们可以编写出更高效、更灵活、更具可读性的Python代码,并且实现类似于内置类型一样的功能。例如,我们可以实现一个自定义列表,类似于Python的list类型,然后使用上述魔术方法来访问、设置和删除元素。同时,我们还可以自定义变量和函数的行为,使我们的Python代码变得更具有表现力和弹性。 总之,了解和掌握Python的魔术方法是Python编程中必不可少的一部分,对于理解和编写实际应用程序非常有价值。在实践中,我们可以根据实际情况选择恰当的魔术方法,从而创建更灵活、更高效的Python类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值