33、编程练习与面向对象编程基础

编程练习与面向对象编程基础

编程练习概述

在编程学习中,通过实际的练习可以更好地掌握各种数据结构和编程技巧。下面将介绍一系列编程练习,涵盖集合、字典的操作,以及文件处理等方面。

集合与字典操作练习
  1. 集合差集操作
    • 创建包含 set1 中但不在 set2 中的元素的集合
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
set3 = set1 - set2
print(set3)
- **创建包含 set2 中但不在 set1 中的元素的集合**
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
set3 = set2 - set1
print(set3)
  1. 筛选集合元素
    假设 set1 引用一个整数集合,set2 引用一个空集合,将 set1 中大于 100 的元素添加到 set2 中。
set1 = {101, 99, 102, 103}
set2 = set()
for element in set1:
    if element > 100:
        set2.add(element)
print(set2)
  1. 字典的序列化与反序列化
    • 将字典序列化并保存到文件
import pickle

dct = {'name': 'John', 'age': 30}
with open('mydata.dat', 'wb') as file:
    pickle.dump(dct, file)
- **从文件中读取并反序列化字典**
import pickle

with open('mydata.dat', 'rb') as file:
    dct = pickle.load(file)
print(dct)
实际应用编程练习
  1. 木星伽利略卫星信息查询程序
    创建三个字典,分别存储木星伽利略卫星的平均半径、表面重力和轨道周期,根据用户输入的卫星名称显示相应信息。
# 平均半径字典
radius_dict = {
    'Io': 1821.6,
    'Europa': 1560.8,
    'Ganymede': 2634.1,
    'Callisto': 2410.3
}

# 表面重力字典
gravity_dict = {
    'Io': 1.796,
    'Europa': 1.314,
    'Ganymede': 1.428,
    'Callisto': 1.235
}

# 轨道周期字典
period_dict = {
    'Io': 1.769,
    'Europa': 3.551,
    'Ganymede': 7.154,
    'Callisto': 16.689
}

moon_name = input("请输入木星伽利略卫星的名称: ")
if moon_name in radius_dict:
    print(f"平均半径: {radius_dict[moon_name]} 公里")
    print(f"表面重力: {gravity_dict[moon_name]} 米/秒²")
    print(f"轨道周期: {period_dict[moon_name]} 天")
else:
    print("输入的卫星名称无效。")
  1. 首都问答程序
    创建一个包含美国各州及其首府的字典,随机询问用户某个州的首府,记录正确和错误回答的数量。
import random

# 州和首府字典
state_capital_dict = {
    'Alabama': 'Montgomery',
    'Alaska': 'Juneau',
    # 其他州和首府信息
}

correct_count = 0
incorrect_count = 0

for _ in range(5):  # 进行 5 次问答
    state = random.choice(list(state_capital_dict.keys()))
    answer = input(f"{state} 的首府是: ")
    if answer == state_capital_dict[state]:
        print("回答正确!")
        correct_count += 1
    else:
        print(f"回答错误,正确答案是 {state_capital_dict[state]}。")
        incorrect_count += 1

print(f"正确回答数量: {correct_count}")
print(f"错误回答数量: {incorrect_count}")
  1. 文件加密与解密程序
    • 加密程序
# 加密字典
codes = {
    'A': '%', 'a': '9', 'B': '@', 'b': '#',
    # 其他字母的加密代码
}

input_file = 'input.txt'
output_file = 'encrypted.txt'

with open(input_file, 'r') as infile, open(output_file, 'w') as outfile:
    content = infile.read()
    encrypted_content = ''.join(codes.get(char, char) for char in content)
    outfile.write(encrypted_content)
- **解密程序**
# 解密字典
decodes = {v: k for k, v in codes.items()}

input_file = 'encrypted.txt'

with open(input_file, 'r') as infile:
    encrypted_content = infile.read()
    decrypted_content = ''.join(decodes.get(char, char) for char in encrypted_content)
    print(decrypted_content)
  1. 唯一单词显示程序
    打开指定文本文件,显示文件中所有唯一的单词。
file_name = 'text.txt'
unique_words = set()

with open(file_name, 'r') as file:
    for line in file:
        words = line.split()
        for word in words:
            unique_words.add(word)

print(unique_words)
  1. 随机数频率统计程序
    生成 100 个 1 到 10 之间的随机数,统计每个数字出现的频率并显示。
import random

frequency_dict = {}
for _ in range(100):
    num = random.randint(1, 10)
    if num in frequency_dict:
        frequency_dict[num] += 1
    else:
        frequency_dict[num] = 1

for num, freq in frequency_dict.items():
    print(f"数字 {num} 出现了 {freq} 次。")
  1. 文件分析程序
    读取两个文本文件,比较它们的内容,显示唯一单词、共同单词、仅在第一个文件中出现的单词、仅在第二个文件中出现的单词以及仅在一个文件中出现的单词。
file1 = 'file1.txt'
file2 = 'file2.txt'

words1 = set()
words2 = set()

with open(file1, 'r') as f1:
    for line in f1:
        words = line.split()
        for word in words:
            words1.add(word)

with open(file2, 'r') as f2:
    for line in f2:
        words = line.split()
        for word in words:
            words2.add(word)

# 所有唯一单词
all_unique_words = words1.union(words2)
print("所有唯一单词:", all_unique_words)

# 共同单词
common_words = words1.intersection(words2)
print("共同单词:", common_words)

# 仅在第一个文件中出现的单词
only_in_file1 = words1 - words2
print("仅在第一个文件中出现的单词:", only_in_file1)

# 仅在第二个文件中出现的单词
only_in_file2 = words2 - words1
print("仅在第二个文件中出现的单词:", only_in_file2)

# 仅在一个文件中出现的单词
either_but_not_both = words1.symmetric_difference(words2)
print("仅在一个文件中出现的单词:", either_but_not_both)
  1. 世界大赛获胜者统计程序
    读取包含 1903 年至 2009 年世界大赛获胜队伍的文件,创建两个字典,一个记录队伍获胜次数,另一个记录每年的获胜队伍。根据用户输入的年份显示该年的获胜队伍和该队伍的获胜次数。
file_name = 'WorldSeriesWinners.txt'

team_wins = {}
year_winners = {}

with open(file_name, 'r') as file:
    year = 1903
    for line in file:
        if year != 1904 and year != 1994:
            team = line.strip()
            if team in team_wins:
                team_wins[team] += 1
            else:
                team_wins[team] = 1
            year_winners[year] = team
        year += 1

user_year = int(input("请输入 1903 到 2009 之间的年份: "))
if user_year in year_winners:
    winner = year_winners[user_year]
    wins = team_wins[winner]
    print(f"{user_year} 年的获胜队伍是 {winner},该队伍共获胜 {wins} 次。")
else:
    print("输入的年份无效或该年未举办世界大赛。")
  1. 蔬菜价格管理程序
    使用字典存储蔬菜名称和价格,提供查看、添加、修改和删除蔬菜信息的菜单,程序退出时将字典序列化保存到文件,启动时从文件中读取并反序列化字典。
import pickle
import os

file_name = 'vegetables.dat'

if os.path.exists(file_name):
    with open(file_name, 'rb') as file:
        vegetables = pickle.load(file)
else:
    vegetables = {}

while True:
    print("\n菜单:")
    print("1. 查看所有蔬菜和价格")
    print("2. 添加新蔬菜和价格")
    print("3. 修改现有蔬菜的价格")
    print("4. 删除现有蔬菜和价格")
    print("5. 退出")
    choice = input("请输入你的选择: ")

    if choice == '1':
        for veg, price in vegetables.items():
            print(f"{veg}: {price} 元")
    elif choice == '2':
        veg = input("请输入蔬菜名称: ")
        price = float(input("请输入蔬菜价格: "))
        vegetables[veg] = price
        print(f"{veg} 已添加。")
    elif choice == '3':
        veg = input("请输入要修改价格的蔬菜名称: ")
        if veg in vegetables:
            price = float(input("请输入新的价格: "))
            vegetables[veg] = price
            print(f"{veg} 的价格已更新。")
        else:
            print(f"{veg} 不存在。")
    elif choice == '4':
        veg = input("请输入要删除的蔬菜名称: ")
        if veg in vegetables:
            del vegetables[veg]
            print(f"{veg} 已删除。")
        else:
            print(f"{veg} 不存在。")
    elif choice == '5':
        with open(file_name, 'wb') as file:
            pickle.dump(vegetables, file)
        break
    else:
        print("无效的选择,请重新输入。")
  1. 简化版 21 点游戏模拟程序
    增强 card_dealer.py 程序,模拟两个虚拟玩家之间的简化版 21 点游戏,根据牌面规则计算玩家手牌分数,直到有玩家手牌分数超过 21 分,另一玩家获胜。
import random

# 牌面和分值
card_values = {
    '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10,
    'J': 10, 'Q': 10, 'K': 10, 'A': [1, 11]
}

# 生成一副牌
suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades']
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
deck = [(rank, suit) for rank in ranks for suit in suits]
random.shuffle(deck)

# 玩家手牌
player1_hand = []
player2_hand = []

# 发牌
def deal_card(hand):
    card = deck.pop()
    hand.append(card)

# 计算手牌分数
def calculate_score(hand):
    score = 0
    aces = 0
    for rank, _ in hand:
        if rank == 'A':
            aces += 1
        score += card_values[rank] if rank != 'A' else 11

    while score > 21 and aces > 0:
        score -= 10
        aces -= 1

    return score

# 游戏循环
while deck:
    deal_card(player1_hand)
    deal_card(player2_hand)

    player1_score = calculate_score(player1_hand)
    player2_score = calculate_score(player2_hand)

    if player1_score > 21 and player2_score > 21:
        print("双方手牌分数都超过 21 分,没有获胜者。")
    elif player1_score > 21:
        print("玩家 2 获胜!")
        break
    elif player2_score > 21:
        print("玩家 1 获胜!")
        break
  1. 单词索引程序
    读取文本文件,创建一个字典,记录每个单词在文件中出现的行号,然后将字典内容写入一个新的文本文件作为单词索引文件。
file_name = 'text.txt'
index_dict = {}

with open(file_name, 'r') as file:
    for line_num, line in enumerate(file, start=1):
        words = line.split()
        for word in words:
            if word in index_dict:
                index_dict[word].append(line_num)
            else:
                index_dict[word] = [line_num]

index_file_name = 'index.txt'
with open(index_file_name, 'w') as index_file:
    for word in sorted(index_dict.keys()):
        line_nums = ', '.join(map(str, index_dict[word]))
        index_file.write(f"{word}: {line_nums}\n")
编程范式:过程式编程与面向对象编程

在编程领域,主要有两种编程范式:过程式编程和面向对象编程。

过程式编程

过程式编程是一种以程序中的过程或动作为中心的编程实践。早期的编程语言大多是过程式的,一个程序由一个或多个过程组成,过程可以看作是执行特定任务的函数,如从用户那里获取输入、进行计算、读写文件、显示输出等。在过程式程序中,数据项通常与过程是分离的,数据项在不同的过程之间传递。随着程序规模的增大和复杂度的提高,数据和操作数据的代码分离可能会导致问题。例如,在一个客户数据库程序中,如果最初设计时客户的姓名、地址和电话号码由三个变量引用,后续需要将这些信息存储在列表中,那么所有涉及这些变量的函数都需要进行修改,这不仅工作量大,还容易引入错误。

面向对象编程

面向对象编程(OOP)则是以对象为中心。对象是一种包含数据和过程的软件实体,对象包含的数据称为数据属性,对象执行的过程称为方法。OOP 通过封装和数据隐藏解决了代码和数据分离的问题。封装是指将数据和代码组合成一个单一的对象,数据隐藏是指对象能够将其数据属性隐藏起来,只有对象的方法可以直接访问和修改这些数据属性。当对象的数据属性被隐藏时,它们可以避免被意外修改,外部代码只需要与对象的方法进行交互,而不需要了解对象数据的格式和内部结构。此外,面向对象编程还具有对象可重用性的优点,一个对象可以被多个程序使用,提高了开发效率。

例如,想象一个闹钟软件对象,它具有当前秒数、分钟数、小时数、闹钟时间和闹钟是否开启等数据属性,以及设置时间、设置闹钟时间、开启闹钟和关闭闹钟等公共方法,还有增加当前秒数、分钟数、小时数和响铃等私有方法。用户只能通过公共方法来操作闹钟对象,而不能直接访问和修改其数据属性。

类与对象

类是创建对象的蓝图,它定义了对象的属性和方法。下面通过几个具体的类来详细介绍类的定义和使用。

硬币类(Coin)
import random

class Coin:
    def __init__(self):
        self.__sideup = 'Heads'

    def toss(self):
        if random.randint(0, 1) == 0:
            self.__sideup = 'Heads'
        else:
            self.__sideup = 'Tails'

    def get_sideup(self):
        return self.__sideup

在这个类中, __init__ 方法是初始化方法,当创建 Coin 类的实例时,它会自动执行,将硬币的初始面设置为正面( Heads )。 toss 方法模拟抛硬币的过程,随机生成 0 或 1 来决定硬币的正反面。 get_sideup 方法用于获取硬币当前的面。为了保护 sideup 属性不被外部代码直接访问和修改,将其命名为 __sideup ,使用双下划线开头表示私有属性。

以下是使用 Coin 类的示例:

my_coin = Coin()
print('这面朝上:', my_coin.get_sideup())
print('我要抛硬币了...')
my_coin.toss()
print('这面朝上:', my_coin.get_sideup())
银行账户类(BankAccount)
class BankAccount:
    def __init__(self, bal):
        self.__balance = bal

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if self.__balance >= amount:
            self.__balance -= amount
        else:
            print('错误:余额不足')

    def get_balance(self):
        return self.__balance

    def __str__(self):
        return f'余额是 ${self.__balance:.2f}'

BankAccount 类模拟了一个银行账户, __init__ 方法接受一个初始余额作为参数,并将其赋值给 __balance 属性。 deposit 方法用于存款, withdraw 方法用于取款,如果余额不足会显示错误信息。 get_balance 方法用于获取当前余额。 __str__ 方法是一个特殊方法,当将 BankAccount 对象传递给 print 函数时,它会自动调用,返回一个表示账户余额的字符串。

以下是使用 BankAccount 类的示例:

start_bal = float(input('请输入初始余额: '))
savings = BankAccount(start_bal)
pay = float(input('你本周的工资是多少? '))
print('我将把它存入你的账户。')
savings.deposit(pay)
print(savings)
cash = float(input('你想取多少钱? '))
print('我将从你的账户中取出。')
savings.withdraw(cash)
print(savings)

通过这些编程练习和类的示例,我们可以更好地理解集合、字典的操作,以及面向对象编程的概念和实践。在实际编程中,根据具体的需求选择合适的编程范式和数据结构,能够提高代码的可读性、可维护性和可扩展性。

编程练习与面向对象编程基础

类的存储与模块化

在实际的编程项目中,随着使用的类数量增多,将类定义存储在模块中是一种常见且有效的组织方式。这样可以使代码更加清晰、易于维护,并且方便在不同的程序中复用这些类。

例如,我们可以将之前定义的 Coin 类存储在一个名为 coin.py 的模块中,代码如下:

import random

class Coin:
    def __init__(self):
        self.__sideup = 'Heads'

    def toss(self):
        if random.randint(0, 1) == 0:
            self.__sideup = 'Heads'
        else:
            self.__sideup = 'Tails'

    def get_sideup(self):
        return self.__sideup

当我们需要在其他程序中使用 Coin 类时,可以通过导入 coin 模块来实现,示例代码如下:

import coin

my_coin = coin.Coin()
print('这面朝上:', my_coin.get_sideup())
print('我要抛硬币了...')
my_coin.toss()
print('这面朝上:', my_coin.get_sideup())

同样,对于 BankAccount 类,我们可以将其存储在 bankaccount.py 模块中,模块内容如下:

class BankAccount:
    def __init__(self, bal):
        self.__balance = bal

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if self.__balance >= amount:
            self.__balance -= amount
        else:
            print('错误:余额不足')

    def get_balance(self):
        return self.__balance

    def __str__(self):
        return f'余额是 ${self.__balance:.2f}'

然后在需要使用该类的程序中导入并使用,示例代码如下:

import bankaccount

start_bal = float(input('请输入初始余额: '))
savings = bankaccount.BankAccount(start_bal)
pay = float(input('你本周的工资是多少? '))
print('我将把它存入你的账户。')
savings.deposit(pay)
print(savings)
cash = float(input('你想取多少钱? '))
print('我将从你的账户中取出。')
savings.withdraw(cash)
print(savings)
面向对象编程的优势总结

面向对象编程(OOP)相较于过程式编程具有多方面的优势,以下是详细总结:
| 优势 | 描述 |
| ---- | ---- |
| 封装性 | 通过将数据和操作数据的方法封装在一个对象中,实现了数据的隐藏和保护。外部代码只能通过对象提供的公共方法来访问和修改数据,避免了数据被意外修改,提高了代码的安全性和可靠性。例如,在 Coin 类和 BankAccount 类中,使用双下划线开头的属性(如 __sideup __balance )将数据属性隐藏起来,只有类内部的方法可以直接访问和修改这些属性。 |
| 可维护性 | 当程序规模增大时,面向对象编程的结构更加清晰,易于理解和维护。每个对象都有自己独立的功能和职责,修改一个对象的内部实现不会影响到其他对象,降低了代码的耦合度。例如,在修改 BankAccount 类的 withdraw 方法时,只需要关注该方法内部的逻辑,而不会对其他类或方法产生影响。 |
| 可扩展性 | 面向对象编程支持继承和多态等特性,使得代码具有良好的扩展性。可以通过继承现有类来创建新的类,复用父类的代码,并添加新的功能。例如,可以创建一个 SavingsAccount 类继承自 BankAccount 类,并在其基础上添加利息计算等功能。 |
| 可重用性 | 对象可以被多个程序重复使用,提高了开发效率。一个经过测试和验证的对象可以在不同的项目中使用,减少了重复开发的工作量。例如, Coin 类和 BankAccount 类可以在不同的模拟程序中使用。 |

编程练习的重要性

编程练习在学习编程过程中具有不可替代的重要性,以下是具体阐述:
- 加深知识理解 :通过实际编写代码解决各种问题,可以更加深入地理解编程语言的语法、数据结构和算法等知识。例如,在进行集合和字典操作的编程练习时,能够更好地掌握集合的差集、交集等操作,以及字典的键值对存储和查找等功能。
- 提高编程技能 :不断地进行编程练习可以提高编程的熟练度和技能水平,包括代码的编写速度、代码的质量和调试能力等。在解决复杂问题的过程中,需要运用各种编程技巧和方法,从而锻炼了自己的编程思维和能力。
- 培养解决问题的能力 :编程练习通常会给出一些实际的问题场景,需要我们分析问题、设计解决方案并编写代码实现。通过不断地解决这些问题,可以培养我们的逻辑思维和解决实际问题的能力。
- 积累项目经验 :完成一系列的编程练习可以积累项目经验,了解实际项目的开发流程和规范。在练习过程中,需要考虑代码的结构、可读性、可维护性等方面,这些都是实际项目开发中需要关注的重要因素。

总结与展望

通过本文介绍的一系列编程练习和面向对象编程的相关知识,我们对集合、字典的操作,以及面向对象编程的概念、类的定义和使用等有了更深入的理解。在实际编程中,我们可以根据具体的需求选择合适的编程范式和数据结构,充分发挥它们的优势,提高代码的质量和开发效率。

未来,随着编程技术的不断发展,面向对象编程仍然会是一种重要的编程范式,并且会与其他编程范式(如函数式编程)相结合,产生更加灵活和强大的编程方式。同时,随着人工智能、大数据等领域的发展,对编程能力的要求也会越来越高,我们需要不断学习和实践,提升自己的编程水平,以适应未来的发展需求。

以下是一个简单的 mermaid 流程图,展示了面向对象编程中创建对象和调用方法的基本流程:

graph TD;
    A[定义类] --> B[创建对象];
    B --> C[调用对象方法];
    C --> D{方法执行结果};
    D -- 成功 --> E[输出结果];
    D -- 失败 --> F[处理错误];

通过这个流程图,我们可以更直观地理解面向对象编程中对象的创建和方法调用的过程。在实际编程中,我们可以根据这个流程来设计和实现自己的程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值