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())
View Code
MyClass 类的属性 i 为: 12345
MyClass 类的方法 f 输出为: hello world

Process finished with exit code 0
View Code

四、构造方法

用于初始化类的内容属性的,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()
View Code

(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/p/8136826.html