面向对象技术简介
- 类(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())
View Code

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()
实例:self调用字段和方法,在任何地方

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#的多态
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 “鸭子类型”
Python “鸭子类型”
参考:http://www.cnblogs.com/wupeiqi/articles/5017742.html
作者:杨永明
出处:https://www.cnblogs.com/ming5218/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。