【1】数据类型1

什么是面对向语言

面向对象编程语言(Object-Oriented Programming Language,简称OOP语言)是一种基于"对象"概念的编程范式。这种编程风格强调使用对象来模拟现实世界中的事物,并通过对象之间的交互来实现软件功能。面向对象编程语言的主要特点包括:

1。抽象:通过抽象,可以将复杂的现实世界问题简化为易于理解和处理的模型。在面向对象编程中,抽象是通过类(class)来实现的,类是对现实世界中事物的抽象描述。

2.封装:封装是指将数据和数据操作方法捆绑在一起,形成一个独立的单元,即对象。封装可以隐藏对象的内部实现细节,只对外提供必要的接口,从而降低模块间的耦合度。

3.继承:继承是一种允许新创建的类(子类)继承现有类(父类)的属性和方法的机制。通过继承,可以实现代码的重用,提高开发效率。

4.多态:多态是指允许不同类的对象对同一消息做出响应的能力。多态性使得程序具有更好的灵活性和可扩展性。

常见的面向对象编程语言有:Java、C++、Python、C#、Ruby等。这些语言都支持上述面向对象编程的基本特性,并在此基础上发展出了各自独特的特性和库。

什么是魔术方法

在 Python 中,魔术方法(Magic Methods)是一种特殊的方法,它们的名称以双下划线 __ 开头和结尾(例如 initstr),也被称为 "双下划线方法"(Dunder Methods)。

魔术方法和普通函数一样,都是用 def 关键字定义的方法,但它们的核心区别在于调用方式和设计目的。 普通方法:需要显式调用,比如 obj.method() 魔术方法:通常不需要手动调用,而是在特定场景下由 Python 解释器自动触发

python 复制代码
class Person:
    def __init__(self, name):  # 魔术方法
        self.name = name
    
    def greet(self):  # 普通方法
        return f"Hello, {self.name}!"

p = Person("Alice")  # 自动触发 __init__(无需写 p.__init__())
print(p.greet())     # 必须显式调用 greet()

参数传递: 传入了一个实参 "Alice"(字符串),Python 解释器会自动在参数列表前添加实例本身作为第一个参数(即 self),因此实际调用 init 时的参数是:self = p(新创建的实例),name = "Alice"。

self 的特殊作用: 所有实例方法(包括魔术方法)的第一个参数必须是 self(名称约定,不是关键字),self 代表当前实例对象本身,在调用方法时由 Python 自动传入,通过 self.属性名 可以访问或修改实例的属性。

构建一副扑克牌

python 复制代码
import collections  # 导入collections模块,用于创建命名元组
# 使用namedtuple创建一个名为Card的命名元组类型,包含rank(点数)和suit(花色)两个字段
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:  # 定义一个FrenchDeck类,用于表示一副法国扑克牌
    # 定义牌的点数列表,包括2-10以及J、Q、K、A
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    # 定义牌的花色列表,包括黑桃、红心、方块和梅花
    suits = ['♠️', '♥️', '♦️', '♣️']

    def __init__(self):  # 初始化方法,创建一副完整的扑克牌
        # 使用列表推导式创建所有可能的牌组合,先按花色排序,再按点数排序
        self._cards = [Card(rank, suit) for suit in self.suits
                      for rank in self.ranks]

    def __len__(self):  # 实现len()函数,返回牌的数量
        return len(self._cards)  # 返回_cards列表的长度

    def __getitem__(self, position):
        return self._cards[position]  # 返回指定位置的牌

定义单张牌的结构(Card 命名元组) Card = collections.namedtuple('Card', ['rank', 'suit']) 这行代码创建了一种 "单张牌" 的类型,每张牌有两个属性:rank:点数,suit:花色

定义整副牌的规则(FrenchDeck 类) ranks 列表:定义所有可能的点数(2-10、J、Q、K、A) suits 列表:定义所有花色(4 种)

生成完整的牌组(init 方法) self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] 这行代码用嵌套循环生成了所有可能的牌: 先遍历每个花色 对每个花色,遍历所有点数(2、3、...、A) 每组合一个(点数 + 花色),就创建一张 Card 实例 最终生成 13 个点数 × 4 种花色 = 52 张牌,存在 _cards 列表中。

支持 "查数量" 和 "取牌" 操作 len 方法:让我们可以用 len(deck) 直接知道牌的总数(52 张) getitem 方法:让我们可以用 deck[0] 取第 1 张牌,deck[-1] 取最后 1 张牌

场景 1:想知道一副牌有多少张 不用魔术方法:得写一个普通方法,比如get_length(),每次用都要显式调用:

python 复制代码
class FrenchDeck:
    def get_length(self):  # 普通方法
        return len(self._cards)

deck = FrenchDeck()
print(deck.get_length())  # 必须写方法名,还得加括号

用魔术方法__len__:直接用 Python 原生的len()函数,不用记类的自定义方法名:

python 复制代码
print(len(deck))  # 所有能算"长度"的东西(列表、字符串、字典、你的牌组)都用len(),统一又好记

场景 2:想取第 10 张牌 不用魔术方法:得写get_card(position)这样的普通方法:

python 复制代码
def get_card(self, pos):
    return self._cards[pos]

print(deck.get_card(10))  # 每次取牌都要写方法名

用魔术方法__getitem__:直接用 deck[10] 就行,不用记方法名:

python 复制代码
print(deck[10])  # 索引从0开始,所以第10张牌是索引为10的牌

getitem 方法之所以能让这摞牌(FrenchDeck 实例)变得可迭代(可以用 for 循环遍历),核心原因是 Python 会自动利用 getitem 实现迭代逻辑,不需要我们手动编写额外的迭代方法。

具体原理: 用 for card in deck: 遍历一副牌时,Python 实际上在做一件很简单的事:从位置 0 开始,不断调用 getitem (position) 取元素,直到取到最后一个位置。 步骤分解如下: 第一次循环:调用 deck.getitem (0),拿到第 0 张牌 第二次循环:调用 deck.getitem(1),拿到第 1 张牌 继续下去:直到 position 超过牌的总数(51),此时会触发 IndexError 异常,Python 会自动捕获这个异常并结束循环

为什么 getitem 能 "兼职" 迭代功能? Python 的迭代机制有一个 "偷懒" 的设计:如果一个对象没有实现专门的迭代方法(iter ),但实现了 getitem ,就会默认用 getitem 来迭代,从索引 0 开始依次获取元素。 这就像:如果一个容器没有 "专门的迭代工具",但有 "按序号取东西" 的功能,那我们就可以从第 0 个开始一个个拿,直到拿完为止。

#doctest: +ELLIPSIS 是 Python 中 doctest 模块的一个选项标记,用于在文档测试(doctest)中启用省略号匹配模式。 它的作用是:让测试时可以用 ...(省略号)匹配字符串中任意长度的任意字符(包括空字符),灵活处理那些 "部分内容不固定" 的输出结果。 举个例子理解 假设你有一个函数返回当前时间,输出中包含不固定的时间值:

python 复制代码
import datetime

def get_current_time():
    """
    返回当前时间的字符串表示。
    
    >>> get_current_time()  # doctest: +ELLIPSIS
    '2023-10-09 15:...'
    """
    return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

这里的 ... 会匹配任意分钟和秒数(比如 15:30:45 或 15:01:59 都能匹配 15:...),避免因时间动态变化导致测试失败。 为什么需要它? 当函数输出中包含动态变化的内容(如时间、内存地址、随机数等),直接写固定的预期结果会导致测试频繁失败。 +ELLIPSIS 允许我们用 ... 模糊匹配这些不固定的部分,只校验关键的固定内容,让文档测试更灵活。

在 Python 中,迭代的 "隐式" 主要体现在:我们很少直接写出 "迭代的具体步骤",而是通过简洁的语法(如 for 循环)触发迭代,背后的细节由 Python 自动处理。 举个直观的例子对比: 假设我们要遍历一副牌(deck)中的所有牌: 显式迭代(手动控制步骤): 如果没有迭代机制,我们需要手动写循环逻辑,明确控制 "从哪开始、到哪结束、每次怎么取":

python 复制代码
position = 0
while position < len(deck):
    card = deck[position]  # 手动获取当前位置的牌
    print(card)
    position += 1  # 手动更新位置

这里的每一步(初始化位置、判断是否结束、取元素、更新位置)都需要我们显式写出,非常繁琐。 隐式迭代(Python 自动处理): 有了迭代机制后,我们只需要写:

python 复制代码
for card in deck:
    print(card)

这里没有出现 "位置""索引""循环条件" 等细节 ------Python 自动帮我们完成了 "从 0 开始取元素、依次递增位置、直到结束" 的全过程。 我们看不到迭代的具体步骤,却能直接使用迭代的结果,这就是 "隐式" 的核心含义。 更深层的原因:迭代器协议的 "自动生效" Python 中,一个对象能被迭代(如 for 循环、in 运算符等),是因为它遵循了 "迭代器协议"------ 但我们通常不需要手动实现完整的协议,很多时候像 FrenchDeck 那样只定义 getitem 就够了。 Python 会自动检测对象是否支持迭代: 如果有 iter 方法(迭代器协议的核心),就用它生成迭代器; 如果没有,就尝试用 getitem 按索引迭代(像牌组的例子); 整个过程不需要我们调用任何迭代相关的方法(如 next),Python 内部自动完成。

排序

完整代码

python 复制代码
import collections  # 导入collections模块,用于创建命名元组
# 使用namedtuple创建一个名为Card的命名元组类型,包含rank(点数)和suit(花色)两个字段
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:  # 定义一个FrenchDeck类,用于表示一副法国扑克牌
    # 定义牌的点数列表,包括2-10以及J、Q、K、A
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    # 定义牌的花色列表,包括黑桃、红心、方块和梅花
    suits = ['♠️', '♥️', '♦️', '♣️']

    def __init__(self):  # 初始化方法,创建一副完整的扑克牌
        # 使用列表推导式创建所有可能的牌组合,先按花色排序,再按点数排序
        self._cards = [Card(rank, suit) for suit in self.suits
                      for rank in self.ranks]

    def __len__(self):  # 实现len()函数,返回牌的数量
        return len(self._cards)  # 返回_cards列表的长度

    def __getitem__(self, position):
        return self._cards[position]  # 返回指定位置的牌
#排序
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)  # 定义花色的优先级,黑桃最高,梅花最低

def spades_high(card):  # 定义一个函数,用于按花色优先级和点数排序
    rank_value = FrenchDeck.ranks.index(card.rank)  # 获取牌的点数在ranks列表中的索引
    return rank_value * len(suit_values) + suit_values[card.suit]  # 返回按花色优先级和点数排序的结果

# 修正花色与权重的对应关系(用符号作为键)
suit_values = {'♠️': 3, '♥️': 2, '♦️': 1, '♣️': 0}

deck = FrenchDeck()
print("排序后前5张:", sorted(deck, key=spades_high)[:5])
相关推荐
蔗理苦2 小时前
2025-10-07 Python不基础 19——私有对象
开发语言·python·私有对象
可触的未来,发芽的智生3 小时前
触摸未来2025.10.04:当神经网络拥有了内在记忆……
人工智能·python·神经网络·算法·架构
蔗理苦4 小时前
2025-10-07 Python不基础 20——全局变量与自由变量
开发语言·python
xiaohanbao094 小时前
理解神经网络流程
python·神经网络
韩立学长4 小时前
【开题答辩实录分享】以《基于Python的旅游网站数据爬虫研究》为例进行答辩实录分享
python·旅游
小熊出擊6 小时前
【pytest】finalizer 执行顺序:FILO 原则
python·测试工具·单元测试·pytest
tao3556676 小时前
【Python刷力扣hot100】49. Group Anagrams
开发语言·python·leetcode
韩立学长6 小时前
【开题答辩实录分享】以《基于Python的新能源汽车管理系统的设计与实现》为例进行答辩实录分享
python·新能源汽车
Pocker_Spades_A7 小时前
中秋与代码共舞:用Python、JS、Java打造你的专属中秋技术盛宴
python