文章目录
在面向对象编程的世界里,程序中的 数据和操作数据的函数是一个逻辑上的整体,我们称之为 对象, 对象可以接收消息,解决问题的方法就是 创建对象并向对象发出各种各样的消息;通过消息传递,程序中的多个对象可以协同工作,这样就能构造出复杂的系统并解决现实中的问题。
类和对象
面向对象编程:把一组数据和处理数据的方法组成对象,把行为相同的对象归纳为类,通过封装隐藏对象的内部细节,通过继承实现类的特化与泛化,通过多态实现基于对象类型的动态分派。
类是一个抽象的概念,对象是一个具体的概念。类是对象的蓝图和模板,对象是类的实例。
在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类。对象的属性是对象的静态特征,对象的行为是对象的动态特征。按照上面的说法,如果我们把拥有共同特征的对象的属性和行为都抽取出来,就可以定义出一个类。
面向对象的过程
面向对象编程:
1、定义类 —> 类的命名使用驼峰命名法(每个单词首字母大写)
— 数据抽象:找到对象相关的静态特征(属性) —> 找名词
— 行为抽象:找到和对象相关的动态特征(方法) —> 找动词
2、造对象
3、发消息
定义类
class Student:
# 数据抽象
def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age
# 行为抽象
def eat(self):
"""吃饭"""
print(f'{self.name}正在吃饭')
def study(self, course_name):
print(f'{self.name}正在学习{course_name}.')
def play(self, game_name):
print(f'{self.name}正在玩{game_name}.')
创建和给对象发消息
# 创建对象 ---> 构造器语法 ---> 类名()
stu1 = Student('小红', 17)
stu2 = Student('小白', 19)
# 给对象发消息(调用对象的方法)
Student.study(stu2, 'Python程序设计') # 小白正在学习Python程序设计
stu1.eat() # 小红正在吃饭
stu2.play('王者荣耀') # 小红正在玩王者荣耀
# 修改年龄
stu1.age = 20
打印对象
上面我们通过__init__
方法在创建对象时为对象绑定了属性并赋予了初始值。在Python中,以两个下划线__
(读作dunder
)开头和结尾的方法通常都是有特殊用途和意义的方法,我们一般称之为魔术方法或魔法方法。如果我们在打印对象的时候不希望看到对象的地址而是看到我们自定义的信息,可以通过在类中放置__repr__
魔术方法来做到,该方法返回的字符串就是用print
函数打印对象的时候会显示的内容,代码如下所示。
class Student:
# 数据抽象
def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age
# 行为抽象
def eat(self):
"""吃饭"""
print(f'{self.name}正在吃饭')
def study(self, course_name):
print(f'{self.name}正在学习{course_name}.')
def play(self, game_name):
print(f'{self.name}正在玩{game_name}.')
def __repr__(self):
return f'{self.name}: {self.age}'
def main():
stu1 = Student('小青', 20)
print(stu1) # 小青: 20
students = [stu1, Student('小蓝', 16), Student('小白', 25)]
print(students) # [小青: 40, 小蓝: 16, 小白: 25]
if __name__ == '__main__':
main()
面向对象编程的支柱
面向对象编程的四大支柱:
- 抽象:提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
- 封装:把数据和操作数据的函数从逻辑上组成一个整体(对象)。隐藏实现细节,暴露简单的调用接口。
- 继承:扩展已有的类创建新类,实现对已有类的代码复用。
- 多态:给不同的对象发出同样的消息,不同的对象执行了不同的行为。
经典案例
例子1:定义一个类描述数字时钟,可以显示时/分/秒,可以运转(走字)
import os
from time import sleep
class Clock:
def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
def show(self):
"""显示当前时间"""
return f'{self.hour:0>2d}:{self.minute:0>2d}:{self.second:0>2d}'
def run(self):
"""走字"""
self.second += 1
if self.second == 60:
self.second = 0
self.minute += 1
if self.minute == 60:
self.minute = 0
self.hour += 1
if self.hour == 24:
self.hour = 0
if __name__ == '__main__':
c1 = Clock(1, 58, 58)
while True:
os.system('cls')
print(c1.show())
sleep(1)
c1.run()
注意:运行时使用终端运行
python 文件名
命令。
例子2:扑克游戏:四个玩家参与,先洗牌,再把牌发到四个玩家手上
-
分析:由题目可以抽取出三个类,分别是牌类、扑克类、玩家类,各自的属性和行为如下:
-
牌(Card)
-
属性:花色(suite)、点数(face)
-
行为:显示
-
-
扑克(Poker)
- 属性:保存牌的列表
- 行为:洗牌(shuffle)、发牌(deal)
-
玩家(Player)
- 属性:名字(昵称)、保存玩家手牌的列表
- 行为:摸牌(get)、整理(arrange)
魔术方法(魔法方法)—> 有特殊用途和意义的方法
~__init__
—> 初始化方法,在调用构造器语法创建对象的时候会被自动调用
~ __str__
—> 获得对象的字符串表示,在调用print函数输出对象时会被自动调用
~ __repr__
—> 获得对象的字符串表示,把对象放到容器中调用print输出时会自动调用
—> representation
~ __lt__
—> 在使用 < 运算符比较两个对象大小时会自动调用
如果一个变量的取值只有有限个选项,可以考虑使用枚举类型。
Python中没有定义枚举类型的语法,但是可以通过继承Enum
类来实现枚举类型。
结论1:枚举类型是定义符号常量的最佳选择!!!
结论2:符号常量(有意义的名字)总是优于字面常量!!!
牌类
"""
example06 - 扑克游戏,四个玩家参与,先洗牌,再把牌发到四个玩家的手上。
Author: yucui
Date: 2021/8/5
"""
from enum import Enum
# 枚举类型
class Suite(Enum):
SPADE, HEART, CLUB, DIAMOND = range(4)
class Card:
"""牌"""
def __init__(self, suite, face):
self.suite = suite
self.face = face
def __str__(self):
return self.show()
def __repr__(self):
return self.show()
def __lt__(self, other):
if self.suite == other.suite:
return self.face < other.face
return self.suite.value < other.suite.value
def show(self):
"""显示"""
suites = ['♠', '❤', '♣', '♦']
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
return f'{suites[self.suite.value]}{faces[self.face]}'
def main():
"""程序入口"""
card1 = Card(Suite.HEART, 1)
card2 = Card(Suite.SPADE, 13)
print(card1, card2)
print(card1 is card2)
card3 = Card(Suite.DIAMOND, 9)
card4 = Card(Suite.CLUB, 11)
print(card3.show(), card4.show())
card1 = card2
print(card1, card2, card3)
# 身份运算符
print(card1 is card2)
print(card1 is card3)
cards = [card1, card2, card3, card4]
print(cards)
if __name__ == '__main__':
main()
扑克类
"""
example07 - 扑克
Author: yucui
Date: 2021/8/5
"""
import random
from example06 import Card
from example06 import Suite
class Poker:
"""扑克"""
def __init__(self):
self.cards = [Card(suite, face)
for suite in Suite
for face in range(1, 14)]
self.counter = 0
def shuffle(self):
"""洗牌"""
self.counter = 0
random.shuffle(self.cards)
def deal(self) -> Card:
"""发牌"""
card = self.cards[self.counter]
self.counter += 1
return card
def has_more(self) -> bool:
"""是否还有牌"""
return self.counter < len(self.cards)
def main():
poker = Poker()
poker.shuffle()
while poker.has_more():
print(poker.deal(), end=' ')
if __name__ == '__main__':
main()
玩家类
"""
example08 - 玩家
Author: yucui
Date: 2021/8/5
"""
from example07 import Poker
class Player:
"""玩家"""
def __init__(self, nickname):
self.nickname = nickname
self.cards = []
def get_one_card(self, card):
"""摸一张牌"""
self.cards.append(card)
def arrange(self):
"""整理手上的牌"""
self.cards.sort()
def show(self):
"""显示玩家手上的牌"""
print(self.nickname, end=': ')
for card in self.cards:
print(card, end=' ')
print()
def main():
nicknames = ('东邪', '西毒', '南帝', '北丐')
players = [Player(nickname) for nickname in nicknames]
poker = Poker()
poker.shuffle()
# 将牌发到四个玩家的手上
for _ in range(13):
for player in players:
card = poker.deal()
player.get_one_card(card)
# 显示四个玩家手上的牌
for player in players:
player.arrange()
player.show()
if __name__ == '__main__':
main()