导航
导航
文章目录
  1. 11.1 简介
  2. 11.2 self
  3. 11.3 类
    1. 创建一个类
    2. 输出
    3. 如何工作
  4. 11.4 对象的方法
    1. 输出
    2. 如何工作
  5. 11.5 __init__方法
  6. 使用__init__方法
    1. 输出
    2. 如何工作
  7. 11.6 类与对象的方法
    1. 使用类与对象的变量
    2. 输出
    3. 如何工作
  8. 11.7 继承
    1. 使用继承
    2. 输出
    3. 如何工作

简明Python教程-11.面向对象的编程

11.1 简介

目前为止的程序都是 面向过程 的。把数据和功能结合起来,用称为对象的东西包裹起来组织程序的方法,称为 面向对象 编程
类和对象是面向对象编程的两个主要方面:类创建一个新类型,对象是这个类的实例
对象可以使用属于对象的变量,来存储数据,属于一个对象或类的变量称为
对象可以使用属于类的函数,来具有功能,属于一个类的函数称为 方法
域和方法合称为类的 属性
域有两种类型,属于每个实例/类的,称为 实例变量 ,属于类本身的,称为 类变量
类使用class关键字创建
类的域和方法被列在一个缩进块中

11.2 self

类的方法与普通函数只有一个特别的区别:
类的方法必须有一个额外的第一个参数名称,但在调用时为这个参数赋值,Python会提供这个值
这个特别的变量指对象本身,按照惯例它的名称是self,虽然可以指定任何名称,但是强烈建议使用self,使得程序读者和IDE可以迅速识别它
例如有一个名为MyClass的类,和该类的一个实例MyObject,当调用MyObject(arg1, arg2)时,Python会自动转为MyClass.method(MyObject, arg1, arg2)
如果有一个不需要参数的方法,还是得给这个方法定义一个self参数

11.3 类

创建一个类

1
2
3
4
5
6
7
8
#!/usr/bin/python
# Filename: simplestclass.py

class Person:
pass # An empty block

p = Person()
print p

输出

1
2
$ python simplestclass.py
<__main__.Person instance at 0xf6fcb18c>

如何工作

使用class语句后跟类名,创建一个新类,后面的缩进的语句块行成类体,例子中使用了空白块,由pass语句表示
类名后跟圆括号来创建一个对象/实例
为了验证,打印变量的类型,显示__main__模块中有了一个Person类的实例,同时打印了存储对象的计算机内存地址

11.4 对象的方法

对象的方法与函数的区别只是一个额外的self变量
使用对象的方法

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/python
# Filename: method.py

class Person:
def sayHi(self):
print 'Hello, how are you?'

p = Person()
p.sayHi()

# This short example can also be written as Person().sayHi()

输出

1
2
$ python method.py
Hello, how are you?

如何工作

sayHi方法没有任何参数,但仍然在函数定义时有self

11.5 __init__方法

Python的类中,有很多方法的名字有特殊的意义
__init__方法在类的一个对象被建立时,马上运行,可以用来做一些对象的 初始化 ,类似于Java中的构造方法
注意名称的开始和结尾都是双下划线

使用__init__方法

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
# Filename: class_init.py

class Person:
def __init__(self, name):
self.name = name
def sayHi(self):
print 'Hello, my name is', self.name

p = Person('Swaroop')
p.sayHi()

# This short example can also be written as Person('Swaroop').sayHi()

输出

1
2
$ python class_init.py
Hello, my name is Swaroop

如何工作

把__init__方法定义为取一个参数name(以及普通的参数self),并创建一个新的域,也称为name,它们是两个不同的变量,通过点号区分
程序中并没有调用__init__方法,只在创建类的新实例的时候,把参数包括在圆括号内,跟在类名后面,从而传递给__init__方法
接下来就可以在sayHi方法中使用self.name域了

11.6 类与对象的方法

已经讨论了类与对象的功能部分,现在来看它的数据部分,它们是与类和对象的命名空间绑定的普通变量,即只在类与对象中有效
有两种域——类的变量,和对象的变量,根据类还是对象拥有这个变量区分
类的变量,由一个类的所有对象/实例共享使用,只有一个类变量拷贝,所以某个对象对类的变量做了修改,会反映到所有其他实例
对象的变量,由类的每个对象/实例拥有,每个对象有自己对这个域的一份拷贝,即它们不是共享的

使用类与对象的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/python
# Filename: objvar.py

class Person:
'''Represents a person.'''
population = 0

def __init__(self, name):
'''Initializes the person's data.'''
self.name = name
print '(Initializing %s)' % self.name

# When this person is created, he/she
# adds to the population
Person.population += 1

def __del__(self):
'''I am dying.'''
print '%s says bye.' % self.name

Person.population -= 1

if Person.population == 0:
print 'I am the last one.'
else:
print 'There are still %d people left.' % Person.population

def sayHi(self):
'''Greeting by the person.

Really, that's all it does.'''
print 'Hi, my name is %s.' % self.name

def howMany(self):
'''Prints the current population.'''
if Person.population == 1:
print 'I am the only person here.'
else:
print 'We have %d persons here.' % Person.population

swaroop = Person('Swaroop')
swaroop.sayHi()
swaroop.howMany()

kalam = Person('Abdul Kalam')
kalam.sayHi()
kalam.howMany()

swaroop.sayHi()
swaroop.howMany()

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
$ python objvar.py
(Initializing Swaroop)
Hi, my name is Swaroop.
I am the only person here.
(Initializing Abdul Kalam)
Hi, my name is Abdul Kalam.
We have 2 persons here.
Hi, my name is Swaroop.
We have 2 persons here.
Abdul Kalam says bye.
There are still 1 people left.
Swaroop says bye.
I am the last one.

如何工作

population变量属于Person类,是一个类的变量;name变量属于对象(使用self赋值),是一个类的变量
__init__方法用一个名字来初始化Person实例,并在population上加1,因为增加了一个人
self.name根据每个对象指定,表明它作为对象的变量的本质。能使用self变量来引用同一个对象的变量和方法,称为 属性引用
docstring对于类和方法同样有用,可以在运行时使用Person.__doc__和Person.sayHi.__doc__,来分别访问类和方法的文档字符串
__del__方法在对象消逝(即对象不再使用,其所占用的内存将返回系统)的时候被调用,在该方法里,将Person.population减1
然而很难保证__del__方法在什么时候运行,使用del语句可以指明它的运行
注释:
相对于Java,Python中所有的类成员(包括数据成员)都是 公共的 ,所有方法都是 有效的
有一个例外,当数据成员名称以 双下划线前缀 (如__privatevar),则Python会把它作为私有变量
有一个惯例,如果某个变量只想在类或对象中使用,则应以 单下划线前缀 ,而其他名称都将作为公共的,可以被其他类/对象使用。但这只是一个惯例,并不像双下划线前缀那样,是Python所要求的
__del__方法与destructor概念类似

11.7 继承

面向对象编程的好处之一是代码重用,实现重用的方法之一是通过继承机制,继承可以理解成类之间的类型子类型关系
设想一个学校中的教师和学生类,他们有共同的属性,如姓名、年龄和地址,也有转有属性,如教师的薪水、课程和假期,学生的成绩和学费
好的方法是创建一个共同的类SchoolMember,然后让教师和学生类都继承这个共同的类,即它们都是这个类型(类)的子类型,然后再为这些子类型添加转有的属性
这种方法有很多优点:
1、如果增加/改变了SchoolMember中的任何功能,它会自动地反映到子类型中。
例如为教师和学生都添加一个身份证的域,只需要把它加到SchoolMember中。而在一个子类型中做的改动不会影响别的子类型。
2、可以把教师和学生对象都作为SchoolMember对象来使用。
例如在统计学校成员的人数时非常有用。一个子类型在任何需要父类型的场合可以被替换成父类型,即对象可被视作父类的实例,这被称为多态现象
重用父类代码时,不必再在每个子类中重复它
在上例中,SchoolMember被称为基本类超类,Teacher和Student类称为导出类子类

使用继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/python
# Filename: inherit.py

class SchoolMember:
'''Represents any school member.'''
def __init__(self, name, age):
self.name = name
self.age = age
print '(Initialized SchoolMember: %s)' % self.name

def tell(self):
'''Tell my details.'''
print 'Name:"%s" Age:"%s"' % (self.name, self.age),

class Teacher(SchoolMember):
'''Represents a teacher.'''
def __init__(self, name, age, salary):
SchoolMember.__init__(self, name, age)
self.salary = salary
print '(Initialized Teacher: %s)' % self.name

def tell(self):
SchoolMember.tell(self)
print 'Salary: "%d"' % self.salary

class Student(SchoolMember):
'''Represents a student.'''
def __init__(self, name, age, marks):
SchoolMember.__init__(self, name, age)
self.marks = marks
print '(Initialized Student: %s)' % self.name

def tell(self):
SchoolMember.tell(self)
print 'Marks: "%d"' % self.marks

t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 22, 75)

print # prints a blank line

members = [t, s]
for member in members:
member.tell() # works for both Teachers and Students

输出

1
2
3
4
5
6
7
8
$ python inherit.py
(Initialized SchoolMember: Mrs. Shrividya)
(Initialized Teacher: Mrs. Shrividya)
(Initialized SchoolMember: Swaroop)
(Initialized Student: Swaroop)

Name:"Mrs. Shrividya" Age:"40" Salary: "30000"
Name:"Swaroop" Age:"22" Marks: "75"

如何工作

定义类时,将父类的名称作为一个元组,跟在类名称之后,来使用继承
父类的__init__方法使用self变量调用,这样就可以初始化对象的父类部分。这一点很重要——Python不会自动调用父类的constructor,需要手动专门调用它
在方法调用之前加上类名称前缀,然后把self变量及其他参数传递给它
在使用SchoolMember类的tell方法时,把Teacher和Student的实例仅作为SchoolMember的实例
在调用子类型的tell方法时,Python会首先查找对应类型的方法,如果在子类中找不到对应的方法,才会在父类中逐个查找。父类是在类定义的时候在元组中指明的。
如果在继承元组中列了一个以上的类,那么它就被称作多重继承