Python公开课 - 详解面向对象

前言

在C语言中,单纯通过结构化的函数也可以实现很好的软件,顺序思路比较好理解;而C++则以面向对象的思维来进行代码组织,通过定义对象、成员变量、成员函数、以封装、继承和多态等方式来更灵活处理业务逻辑。

Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。

理解对象和实例

对于初学者来说,这两个词太抽象,我们其实可以把他具象化来看。现实生活中,经常会有一些流程规章办法,我们要按照这些规章办法来做事。

例如要考情流程,那么对于考情流程的定义,你可以把它理解就是一个类,而对于你每天实际考勤打卡的情况,就是这个类的实例了。

抽象点理解:类是定义,实例则是依赖于这个定义产生的具体行为集合。

面向对象的基本概念

  • : 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。
  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 成员变量:类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • 成员函数:类中定义的函数。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 局部变量:定义在方法中的变量,只作用于当前实例的类。
  • 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
  • 实例化:创建一个类的实例的过程,也就是创建一个类的具体对象。
  • 对象:通过类定义的数据结构实例。

例如:

class Animal(object):
    version = '1.0'
    def __init__(self, name):
        self.name = name

    def getName(self):
        return self.name


class Cat(Animal):
    def __init__(self, name):
        Animal.__init__(self, name)
        self.nick_name = 'Tom'

    def getNickName(self):
        return self.nick_name

    def getName(self):
        prefix = "override"
        return "[%s:]%s" %(prefix, self.name)

a = Cat('cat')

print(a.getName(), a.nick_name, a.getNickName())

输出:
[override:]cat Tom Tom

示例说明:

  • class: 类定义关键字,即声明以下代码块是一个类
  • version: 类变量
  • name, nick_name: 成员变量
  • getName, getNickName: 成员函数
  • prefix: 成员函数中的局部变量
  • a.nick_name: 实例变量
  • class Cat(Animal)::继承,即Cat继承于Animal
  • a = Cat('cat'): 实例化一个cat对象
  • a: 一个cat对象

封装

我们定义一个类,需要将他的基本属性进行封装,例如定义一个学生类,他包含了名字和年龄两个属性及对应的get/set方法:

class Student:
    def __init__(self):
        self.name = "" 
        self.__age = 0 #定义私有属性,私有属性在类外部无法直接进行访问

    def setName(self, name):
        self.name = name

    def setAge(self, age):
        self.__age = age


    def getName(self):
        return self.name

    def getAge(self):
        return self.__age

o = Student()
o.setName("Alex")
o.setAge(18)

print(o.getName(), o.getAge())
print(o.name)
print(o.__age)


输出:
Alex 18
Alex
Traceback (most recent call last):
  File "test13.py", line 25, in <module>
    print(o.__age)
AttributeError: 'Student' object has no attribute '__age'

示例说明:

  • __init__: 构造函数,该方法在类实例化时会自动调用
  • self: 相当于C++中的this关键字,代表是类的实例
  • __age: 声明该属性为私有,不能在类的外部被使用或直接访问, 同理成员函数如果加上__也是一样

继承与多重继承

在上面的例子中已经说明了Python如何进行继承,下面我们阐述一下如何进行多重继承

class A:
    def say(self):
        print('say hi')

class B:
    def listen(self):
        print('listen story')

class C:
    def eat(self):
        print('eat food')

class Multi(A, B, C):
    def test(self):
        pass

obj = Multi()
obj.say()
obj.listen()
obj.eat()


输出:
say hi
listen story
eat food

注意:

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法

多态

重载与重写

  • 重载是指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同

  • 重写是存在子父类之间的,子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表和相同的返回类型

class A:
    def test1(self, a, b):
        self.a = a
        self.b = b
        print('A.test1')

    def test2(self, c):
        self.c = c
        print('A.test2')


class S(A):
    def test1(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
        print('S.test1')

    def test2(self, c):
        print('S.test2')


a = A()
s = S()

a.test1(1, 2)
a.test2(1)
s.test1(1, 2, 3)
s.test2(1)
s.test1(1, 2)


输出:
A.test1
A.test2
S.test1
S.test2
Traceback (most recent call last):
  File "test15.py", line 30, in <module>
    s.test1(1, 2)
TypeError: test1() missing 1 required positional argument: 'c'

注意:

上面例子可以看出,虽然我们在子类S中重载了父类A的方法test1,但是执行报错了,原因在于Python3中是不支持函数重载,因为 Python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 Python 中很可能是相同的代码,没有必要做成两个不同函数。

也就是说,我们只需要将子类S稍作改动即可:

class S(A):
    def test1(self, *attr):
        self.attr = list(attr)
        print('S.test1 %s' %self.attr)

    def test2(self, c):
        print('S.test2')


输出:
A.test1
A.test2
S.test1 [1, 2, 3]
S.test2
S.test1 [1, 2]

获取类的属性

使用type()

判断对象类型,使用type()函数

>>> type(S)
<class '__main__.S'>

使用isinstance()

判断class的类型,可以使用isinstance()函数

>>>isinstance(s, S)
True
>>>isinstance(s, A)
True

使用dir()

获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list

>>>dir(s)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'attr', 'test1', 'test2']

相关阅读