1.类的生成语法
一个类就是对拥有相同属性的对象的抽象。在类中定义的是这些对象都具备的属性。一个对象就是一个类的实例化
# 定义一个类, class是定义类的语法,TestClass是类名,(object)是新式类的写法 class TestClass(object): # __init__初始化函数,创建对象时的一些属性就在这里赋值 def __init__(self,name,age): self.name = name self.age = age def func(self): print("hello,I name is %s, i am %d years " % (self.name,self.age)) print(TestClass) # <class '__main__.TestClass'>,表示哪怕不进行实例化
上面的__init__叫做构造函数,在类被调用时,这个方法(在类中不叫函数了,叫方法)会自动执行,进行一些初始化的动作。self相当于创建对象的对象自身。如下:
jack = TestClass('jack',12)# #此时self 相当于 Jack , TestClass(Jack, 'jack',12)
在jack = TestClass('jack',12)时,python的解释器干了两件事:
- 在内存中开辟一块空间指向Jack这个变量名
- 调用Role这个类并执行其中的__init__(…)方法,相当于TestClass.__init__(Jack, 'jack',12)。这么做是为了把'jack'、12这2个值跟刚开辟的jack对象关联起来,把创建实例时传入的几个值存到jack的内存空间里。所以在调用__init__方法时,就必须把r1这个变量也传进去,否则__init__不知道要和那个对象关联。
上面的代码中,类中的方法第一个参数也是self,如下:
def func(self): print("hello,I name is %s, i am %d years " % (self.name,self.age)) jack.func() # 相当于testClass.func(jack)
但是调用的时候不用给他传值,为什么?因为,你在func()方法中可能要访问对象的一些其它属性, 比如这里就访问了Jack的名字和age。但是在访问的时候得告诉这个方法访问的是哪个对象,于是就把Jack传给了这个self参数,然后在func里调用 self.name 就相当于调用jack.name 。 说白了就是在调用类中的一个方法时,你得告诉人家你是谁。
总结:
- 上面的jack = TestClass('jack',12)叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了, 这个对象就叫做实例。
- 刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
2.实例变量和类变量
class TestClass(object): name = '类变量' # 主要的作用是节省开销 def __init__(self,name,age): self.name = '实例变量' def func(self): print(self.name) print(TestClass.name) # 类变量 jack = TestClass('jack',12) print(jack.name) # 实例变量 jack.func() # 实例变量 TestClass.age.append(1) jack.age.append(2) print(jack.age,TestClass.age) # [1, 2] [1, 2]
总结:写在类中的变量叫做类变量,写在__init__方法中的变量叫做实例变量。先调用实例变量,后调用类变量。
3.析构函数
析构函数: 在实例释放、销毁的时候自动执行的。通常用于做一些收尾工作, 如关闭一些数据库连接,关闭打开的临时文件等操作。
class TestClass(object): def __init__(self,name): self.name = name def func(self): print(self.name) def __del__(self): # 析构函数 print('析构函数。。。%s',self.name) c1 = TestClass('张三') c2 = TestClass('李四') c1.func() del c2 结果: 张三 析构函数。。。%s 张三 析构函数。。。%s 李四
4.私有属性
私有属性只能在类的内部访问,在外部不能使用类名和实例名称访问。真要访问的话可以使用 实例名._类名私有属性 进行访问。
class TestClass(object): def __init__(self,name,age): self.name = name self.__age = age # 私有属性 def func(self): print(self.__age) c1 = TestClass('张三',12) c1.func() # 12 #print(c1.__age) # AttributeError: 'TestClass' object has no attribute '__age'
print(c1._TestClass__age) # 12 # 私有属性可以使用_TestClass__age访问
print(TestClass.__age) # AttributeError: type object 'TestClass' has no attribute '__age'
5.私有方法
私有方法和私有属性一样,在方法的名称前面加上__就变成了私有方法。
class TestClass(object): def __init__(self,name,age): self.name = name self.__age = age # 私有属性 def __func(self): print(self.__age) def foo(self): self.__func() c1 = TestClass('张三',12) c1.foo() # 12 c1.__func() # AttributeError: 'TestClass' object has no attribute '__func' print(c1._TestClass__func()) # 12 # 私有方法可以使用_TestClass__func()访问 print(TestClass.__func()) # AttributeError: type object 'TestClass' has no attribute '__func'
6.继承
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”,继承的过程,就是从一般到特殊的过程。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
- 实现继承是指使用基类的属性和方法而无需额外编码的能力。
- 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
6.1继承的定义
class Person(object): # 定义一个父类 def talk(self): # 父类中的方法 print("person is talking....") class Chinese(Person): # 定义一个子类, 继承Person类 def walk(self): # 在子类中定义其自身的方法 print('is walking...') c = Chinese() c.talk() # 调用继承的Person类的方法 # person is talking.... c.walk() # 调用本身的方法 # is walking...
6.2 构造函数的继承
继承类的构造方法:
- 经典类的写法: 父类名称.__init__(self,参数1,参数2,...)
- 新式类的写法:super(子类,self).__init__(参数1,参数2,....)
class Person(object): def __init__(self, name, age): self.name = name self.age = age self.weight = 'weight' def talk(self): print("%s is talking...." % self.name) class Chinese(Person): def __init__(self, name, age, language): # 先继承,在重构 Person.__init__(self, name, age) # 继承父类的构造方法,也可以写成: #super(Chinese, self).__init__(name, age) self.language = language # 定义类的本身属性 def walk(self): print('%s is walking...%s year' % (self.name,self.age)) c = Chinese('wl', 22, 'Chinese') c.talk() # wl is talking.... c.walk() # wl is walking...22 year
6.3子类对父类方法的重写
如果我们对基类/父类的方法需要修改,可以在子类中重构该方法,不会先执行父类方法。如下的talk()方法
class Person(object): def __init__(self, name, age): self.name = name self.age = age self.weight = 'weight' def talk(self): print("person is talking....")
class Chinese(Person): def __init__(self, name, age, language): Person.__init__(self, name, age) self.language = language print(self.name, self.age, self.weight, self.language) def talk(self): # 子类 重构方法, # Person.talk(self) # 先执行父类方法 print('%s is son class' % self.name) def walk(self): print('is walking...') c = Chinese('wl', 22, 'Chinese') c.talk() # wl 22 weight Chinese #wl is speaking chinese
6.4多继承
super()函数为新式类的方法,采用新式类要求最顶层的父类一定要继承于object,这样就可以用super()函数来调用父类的init()等函数。每个父类都执行且执行一次,并不会出现重复调用的情况。采用super()方法时,会自动找到第一个多继承中的第一父类。但是如果想要继续调用其它父类init()函数或两个父类的同名函数时,就要用经典类的调用方法了,即 父类名.__init__(self,参数)
class Food(object): '''父类Food''' def __init__(self, name, color): self.name = name self.color = color def eatable(self): print("%s is eaten." % self.name) def appearance(self): print('The color of the %s is %s.' % (self.name, self.color)) class Fruits(object): '''父类Fruits''' def __init__(self, name, nutrition): self.name = name self.nutrition = nutrition def info(self): print("%s can supply much %s." % (self.name, self.nutrition)) class Salad(Fruits, Food): # 继承多个父类 def __init__(self, name, nutrition, color, tasty): super(Salad, self).__init__(name, nutrition) Food.__init__(self, name, color) self.tasty = tasty def taste(self): print("%s is a little %s." % (self.name, self.tasty)) def info(self): '''重写父类的方法时,不会执行父类的同名方法,执行子类中的方法''' print('Salad class info') # Salad class info obj = Salad('bnanae', 'VC', 'yellow', 'sour') obj.eatable() # Food类方法,结果是bnanae is eaten.. obj.appearance() # Food类方法,结果是The color of the orange is orange. obj.info() # Fruits类方法,结果是bnanae can supply much VC. obj.taste() # Salad类方法,结果是bnanae is a little sour.
7.经典类和新式类
- 新式类继承object类,经典类不继承任何类
- 新式类用super关键字继承构造方法,经典类用 父类.__init(self)来继承
- 新式类:广度优先查询,经典类:深度优先查询(因为新式类讲究的是新,所以要找最近的,最新的;然后经典的讲究古老,所以更远更深的)
- 在python3中默认都是新式类。
7.1新式类定义时必须继承object类,继承了object类的就叫做新式类。
class Fruits(object): '新式类' pass
7.2采用super()函数类调用父类的 init()等函数
super(子类名,self).__init__(参数1,参数2,..)
7.3调用父类中相同属性或者方法的顺序
新式类的调用顺序为: 广度优先查询,子类先在自己的所有父类中从左至右查询,如果没有需要的方法或属性,再到本身父类的父类中去查询。
class A(object): def __init__(self): self.n = "A" class B(A): # 注释B中的代码,获得C def __init__(self): super(B, self).__init__() self.n = "B" class C(A): # 注释C中的代码,获得A def __init__(self): super(C, self).__init__() self.n = "C" class D(B, C):# 注释D中的代码,获得B def __init__(self): super(D, self).__init__() self.n = "D" d = D() print(d.n) # D
8.多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给len传字符串就返回字符串的长度,传列表就返回列表长度。
Python多态示例:
class Animal: @staticmethod def animal_talk(obj): obj.talk() class Cat(Animal): def talk(self): print('cat') class Dog(Animal): def talk(self): print('Dog') a = Dog() b = Cat() Animal.animal_talk(a) # Dog Animal.animal_talk(b) # cat