往期回顾:
- [Python] 入门(1):安装 Python 及必要组件
- [Python] 入门(2):Python 基础 – 变量
- [Python] 入门(3):Python 基础 – 内建函数
- [Python] 入门(4):Python 基础 – 列表和词典的方法
- [Python] 入门(5):Python 基础 – 字符串的方法
- [Python] 入门(6):Python 基础 – Python 基础 – 循环,条件和迭代器
- [Python] 入门(7):Python 基础 – 自定义函数
- [Python] 入门(8):Python 基础 – 解析式,生成器和程序错误
- [Python] 入门(9):Python 基础 – 字符串格式化
- [Python] 入门(10):Python 基础 – 面向对象编程(本文)
本文目录
Wikipedia 说:
面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中…面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象,因此它们都可以被看作一个小型的“机器”,即对象。
打个比方,“类”是做汤圆的共同特征,“对象”是已经做好的汤圆。不管汤圆是五仁馅还是芝麻馅,它们都有共同的特征:外面用糯米粉包裹,里面有馅…如果我们把所有汤圆的共同特征都提取出来,这些特征就是类。在开始讨论类以前,我们先了解个概念:
命名空间
命名空间(namespace)是一个从名字到对象的映射。举个例子,假如定义一个函数 sum,那么 sum 就是对应函数的命名空间。这么说可能不好理解,我们来看一个例子:
>>> a = 1
>>> b = 2
>>> def names():
... a = 4
... print("在这个命名空间里 a 的值是:", a)
... global b
... b = 5
... print("在这个命名空间里 b 的值是:", b)
>>> print("在全局命名空间里,a 的值是:", a)
在全局命名空间里,a 的值是: 1
>>> print("在全局命名空间里,b 的值是:", b)
在全局命名空间里,b 的值是: 2
>>> names()
在这个命名空间里 a 的值是: 4
在这个命名空间里 b 的值是: 5
>>> print("现在在全局命名空间里,a 的值是:", a)
现在在全局命名空间里,a 的值是: 1
>>> print("现在在全局命名空间里,b 的值是:", b)
现在在全局命名空间里,b 的值是: 5
解释一下发生了什么:首先,我们在全局命名空间里定义了 a = 1,b =2;然后我们在函数 names 的局部命名空间里重新定义了 a = 3,b = 4,之后分别打印在不同命名空间里的 a 和 b。
我们先来看 a。a 在 names 里被放在了局部命名空间里(这叫做局部赋值),在 names 外被放在了全局命名空间里,两个命名空间互不影响,所以在 names 里面打印出 1,在 names 外面打印出 4。
b 就有点复杂了。我们在 names 加了一个 global
语句,强制 b 被放在全局命名空间里(这叫做全局赋值),这样 names 里的 b 和 names 外的 b 处在同一个命名空间,所以在调用 names 以后,全局命名空间里 b 的值就被覆盖了,所以开始 b 的值是 2,调用函数后再打印,无论在函数的内外打印,b 的值都变成了 5。
默认情况下,在函数里的变量都是局部赋值。
定义一个类很简单:
>>> class ClassName:
... <statement-1>
... .
... .
... .
... <statement-N>
类对象
类对象有两种操作:属性引用和实例化。
属性引用
属性引用使用标准语法 obj.attr。比如我们定义一个类:
>>> class avengers:
... '''复仇者联盟'''
... name = "Iron Man"
...
... def say():
... print("I'm Iron Man!")
>>> print(avengers.name)
Iron Man
>>> avengers.say()
I'm Iron Man!
name
变量成了 avengers 的属性(没有括号),say
函数成了 avengers 的方法(有括号)。
实例化
可以想象成用汤圆机器做了一个汤圆,方法是调用这个类:
avenger = avengers()
如果想创建一个带有特定初始状态的函数,需要在类中包含一个特殊的 __init__()
(一边各有两个下划线)的方法:
>>> class avengers:
... '''复仇者联盟'''
... def _init_(self, a, b):
... self.name = a
... self.age = b
想在这个类里填充数据并调用也非常简单:
>>> x = avengers("Captian America", 101)
>>> x.name, x.age
('Captian America', 101)
以上都是非常简单的示例,现在来个复杂一点的(就复杂那么一点而已):
>>> class avengers:
... '''复仇者联盟'''
... alias = []
... def __init__(self, a):
... self.name = a
...
... def add_alias(self, name):
... self.alias.append(name)
>>> Thor = avengers("Thor")
>>> Thor.add_alias("Son of Odin")
>>> Thor.add_alias("God of Thunder")
>>> Thor.alias
['Son of Odin', 'God of Thunder']
我们先定义了索尔的类,然后分别将他的昵称以方法的形式加在类中,最后以属性的方式调用他的昵称。看起来挺好的,那我们在这个基础上再做一个鹰眼的类:
>>> Barton = avengers("Barton")
>>> Barton.add_alias("Hawkeye")
>>> Barton.add_alias("Arrow Guy")
>>> Barton.alias
['Son of Odin', 'God of Thunder', 'Hawkeye', 'Arrow Guy']
哎不对,鹰眼的昵称怎么还有索尔的?其实是上面定义的类的第三行 alias = []
位置不对,它被定义成了类里的全局变量,应该放到 __init__(self, a)
里成为局部变量。改一下,再试一次:
>>> class avengers:
... '''复仇者联盟'''
... def __init__(self, a):
... self.name = a
... self.alias = []
... def add_alias(self, name):
... self.alias.append(name)
>>> Thor = avengers("Thor")
>>> Thor.add_alias("Son of Odin")
>>> Thor.add_alias("God of Thunder")
>>> Thor.alias
['Son of Odin', 'God of Thunder']
>>> Barton = avengers("Barton")
>>> Barton.add_alias("Hawkeye")
>>> Barton.add_alias("Arrow Guy")
>>> Barton.alias
['Hawkeye', 'Arrow Guy']
这回就对了嘛!
类里还可以加循环和判断、解析式和生成式等等,因为这是入门文章,所以就不说了。学无止境啊~现在 Python 基础知识就正式完成了,下一篇开始会讲各种常用模块,包括内建模块,numpy,pandas 和 matplotlib。
练习(写出生成如下变量的代码或结果):
定义一个叫做 fruit
的类,接受“水果名字”和“水果数量”两个变量。同时放置一个判断,当水果数量小于 5 时,打印“**水果只有**个”;当水果数量多于 5 时,打印“**水果有**个”。示例:
>>> a = fruit("apple", 3)
>>> ... # 自选变量名称
apple only have 3
>>> b = fruit("banana", 6)
>>> ... # 自选变量名称
banana have 6
答案下篇文章公布。欢迎把答案留言与老宅交流、互动!
上一篇练习的答案:
# 1. ' 1.58000000 ' (前后各两个空格);
>>> f"{1.58:^14.8f}"
' 1.58000000 '
# 2. 你现在的时间,格式为 “月/日/四位年 缩写星期几 小时(24 小时制)-分钟-秒”。
>>> import datetime
>>> f"{datetime.datetime.now():%m/%d/%Y (%a) %H-%M-%S}"
'05/31/2019 (Fri) 16-53-45'