python 面向对象
面向对象技术简介
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
一、类
用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
二、对象
对象是类的实例,比如字符串的基类是str(),那么可以以下的操作来实例化这个类,从而获取对象
name = str()
这样就得到了一个那么对象,它具有str类所拥有的的所有属性和方法
例如: peple是一个类,x就是这个类的对象
class peple():
def __init__(self, name):
self.Name = name
def chi(self):
print(self.Name)
x = peple("yangyongming")
三、类对象
类对象支持两种操作:字段引用和实例化。
字段引用使用和 Python 中所有的属性(字段)引用一样的标准语法:obj.name:如下:
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py # !/usr/bin/python3 class MyClass: """一个简单的类实例""" # 这是一个静态字段 i = 12345 # 这是一个静态方法 def f(self): return 'hello world' # 实例化类,得到一个类对象x x = MyClass() # 访问类的属性和方法 print("MyClass 类的属性 i 为:", x.i) print("MyClass 类的方法 f 输出为:", x.f())
MyClass 类的属性 i 为: 12345
MyClass 类的方法 f 输出为: hello world
Process finished with exit code 0
四、构造方法
用于初始化类的内容属性的,Python提供的构造函数式 __init__()。
__init__()方法是可选的。
当该类被实例化的时候就会执行该函数,我们可以把要先初始化的属性放到这个函数里面,如下程序:
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class Complex: # 这是一个构造方法 def __init__(self, realpart, imagpart): # 这是一个普通字段 self.r = realpart self.i = imagpart # 实例化类 x = Complex(3.0, -4.5) # 在这个过程中构造函数就会被自动执行 print(x.r, x.i) # 输出结果:3.0 -4.5
3.0 -4.5
Process finished with exit code 0
五、类中的self参数
类中出现的self字样代表类的实例(对象),而非类本身
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self,当然写成其他的名称也可以,但是还是写成官方的好一些o(* ̄︶ ̄*)o,如下代码:
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt()
<__main__.Test object at 0x000001AA1763C9B0> <class '__main__.Test'> Process finished with exit code 0
从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
self 不是 python 关键字,我们把他换成 其他字符也是可以正常执行的:
在类中,任何加了self的字段或者方法,在类中的任何地方都可以调用,如下:
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class Complex: # 这是一个构造方法 def __init__(self, arg1, arg2): # 这是一个普通字段 self.r = arg1 self.i = arg2 def one(self): print(self.r) def two(self): self.one() a = Complex(100, 200) a.one() a.two()
100
100
Process finished with exit code 0
六、类的成员
类的成员可以分为三大类:字段、方法和属性
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
(1)、类的字段
类的字段有两种,一个是普通字段,一个是静态字段:
静态字段:在类中,方法之外,通过类名来调用。
普通字段:常出现在构造方法内,通过对象来调用。
- 普通字段属于对象
- 静态字段属于类
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class Foo: # 这是一个静态字段 i = "123456" # 这是一个构造方法 def __init__(self, name): # 这是一个普通字段 self.Name = name # 实例化类 r = Foo("MING") # 访问静态字段 print(Foo.i) # 访问普通字段 print(r.Name)
123456
MING
Process finished with exit code 0
通过以上代码可以看出对于静态字段的调用,通过类调用(也可以通过对象调用,但是不推荐使用对象来调用),因为在其他的语言中是不支持这种调用方式。
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
(2)、类的方法
在类地内部,使用 def 关键字来定义一个方法。
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;使用@classmethod标注。
- 静态方法:由类调用;无默认参数;使用@staticmethod标注。
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class peple(): live = "yes" def __init__(self, name): self.Name = name def chi(self): print(self.live) print(self.Name) x = peple("ming") x.chi()
yes
ming
Process finished with exit code 0
普通方法可以使用类中的任何属性
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class peple(): live = "yes" def __init__(self, name): self.Name = name @staticmethod def chi(): print(live) print(self.Name) peple.chi()
Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 17, in <module> peple.chi() File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 15, in chi print(live) NameError: name 'live' is not defined Process finished with exit code 1
静态方法不能使用类中的任何属性
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class peple(): live = "yes" def __init__(self, name): self.Name = name @classmethod def chi(cls): print(cls.live) @classmethod def he(cls): print(cls.Name) peple.chi() peple.he()
Traceback (most recent call last): yes File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 22, in <module> peple.he() File "C:/Users/Administrator/PycharmProjects/untitled2/Practice/11/1.py", line 19, in he print(cls.Name) AttributeError: type object 'peple' has no attribute 'Name' Process finished with exit code 1
类方法只能使用类的静态属性(不带self的),类不能使用方法属性(带self的)
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
(3)、类的属性
。。。。。。
七、类成员修饰符
对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法
私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
#!/usr/bin/env python # -*- coding: utf-8 -* # Created by YangYongming at 2018/11/26 12:27 # FileName: 1.py class ming: chi = "这是公有静态字段" __he = "这是私有静态字段" def __init__(self): self.name = "这是公有普通字段" self.__foo = "这是私有普通字段" def one(self): return "这是公有普通方法" def __two(self): return "这是私有普通方法" x = ming() print(ming.chi) print(x.name) print(x.one())
这是公有静态字段
这是公有普通字段
这是公有普通方法
Process finished with exit code 0
类外访问私有成员:报错
- 公有成员:对象可以访问;类内部可以访问;派生类中可以访问
- 私有成员:仅类内部可以访问;
通过以上代码可以看出,普通方法、静态方法使用成员修饰符后,该方法在类外部是无法访问的,在类内部是可以访问的,该种方法是不可以被继承的
私有的成员也不是绝对不可以访问,我们可以通过下面这种方式访问:
class Mo(): def __one(self): print("one") obj = Mo() obj._Mo__one() # 特殊的访问方式,但是不建议使用 # 运行结果 # one
八、类的特殊成员
(1). __doc__
表示类的描述信息
(2). __init__
构造方法,通过类创建对象时,自动触发执行。
(3). __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
(4). __call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
(5). __dict__
类或对象中的所有成员
(6) . __str__
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
(7) . __class__
表示当前操作的对象的类是什么
(8) . __module__
表示当前操作的对象在那个模块
九、面向对象三大特性
面向对象的三大特性是指:封装、继承和多态。
(1)封装
对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
调用被封装的内容时,有两种情况:
- 通过对象直接调用
- 通过self间接调用
(2)继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,如下所示:
class 猫: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something 伪代码
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
class 动物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 class 猫(动物): def 喵喵叫(self): print '喵喵叫' # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 class 狗(动物): def 汪汪叫(self): print '喵喵叫' 伪代码
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不同而已。
学习了继承的写法之后,我们用代码来是上述阿猫阿狗的功能:
class Animal: def eat(self): print "%s 吃 " %self.name def drink(self): print "%s 喝 " %self.name def shit(self): print "%s 拉 " %self.name def pee(self): print "%s 撒 " %self.name class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def cry(self): print '喵喵叫' class Dog(Animal): def __init__(self, name): self.name = name self.breed = '狗' def cry(self): print '汪汪叫' # ######### 执行 ######### c1 = Cat('小白家的小黑猫') c1.eat() c2 = Cat('小黑的小白猫') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat() 代码实例
那么问题又来了,多继承呢?
- 是否可以继承多个类
- 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
1、Python的类可以继承多个类,Java和C#中则只能继承一个类
2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
class D: def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> D --> C # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar() 经典类多继承
class D(object): def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> C --> D # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar() 新式类多继承
经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
派生类执行基类构造方法的两种方式
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : Jack.Ming class Annimal: def __init__(self, weight,hight): self.Weight = weight self.Hight = hight class Cat(Annimal): def __init__(self, name, weight, hight): self.Name = name super(Cat, self).__init__(weight ,hight) #方式1 def pri(self): print(self.Name,self.Weight,self.Hight) class Dog(Annimal): def __init__(self, name, weight, hight): self.Name = name Annimal.__init__(self, weight,hight) #方式2 def pri(self): print(self.Name,self.Weight,self.Hight) A = Cat("cat", 100,200) A.pri() A = Dog("dog", 100,200) A.pri()
(3)多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj): """Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show Python伪代码实现Java或C#的多态
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' def Func(obj): print obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj) Python “鸭子类型”
参考:http://www.cnblogs.com/wupeiqi/articles/5017742.html