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继承于Animala = 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']