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的解释器其实干了两件事:
-
在内存中开辟一块空间指向r1这个变量名
-
调用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个参数跟谁关联呀。
-
明白了么哥?所以这个__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就可以了。 说白了就是在调用类中的一个方法时,你得告诉人家你是谁。
- 上面的这个role1 = Role("zhangsan", "police", "M16")动作,叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了, 这个对象就叫做实例。
- 刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者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)
- 在Python2中,对于新式类,如果将D注释掉,那么将打印"B";在此基础上,将B注释掉,那么将打印"C":继续将C注释掉,那么将打印"A"。此为广度查询。
- 在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属性