登录
首页 >  文章 >  python教程

用Python数据模型编写Pythonic代码

来源:dev.to

时间:2025-02-17 11:54:48 186浏览 收藏

哈喽!大家好,很高兴又见面了,我是golang学习网的一名作者,今天由我给大家带来一篇《用Python数据模型编写Pythonic代码》,本文主要会讲到等等知识点,希望大家一起学习进步,也欢迎大家关注、点赞、收藏、转发! 下面就一起来看看吧!

特殊方法

>这种明显的奇怪性是冰山一角,当正确理解时,它是我们所谓的pythonic的关键。冰山被称为python数据模型,它描述了您可以用来使自己的对象与最惯用的语言功能效果很好的api。

>关于python数据模型的特别之处。我们没有给出个人答案,而是为什么不深入研究,并通过了解数据模型来了解我们可以完成的工作。数据模型只是谈论如何表示数据。在python中,数据由对象表示或听起来有些技术,对象是python的数据抽象。数据模型为我们提供了一个api,该api允许我们的对象在python编程的“引擎盖下”效果很好。

>在我们深入研究python数据模型中,我们将专门关注特殊方法。特殊方法是具有特殊名称的类功能,这些函数由特殊语法调用。在我们的班级定义中定义这些特殊方法可以为我们的课堂实例提供一些非常酷的python权力,例如迭代,操作员超载,与上下文管理者('with with with with关键字),正确的字符串表示和格式化等良好合作。为了向您展示如何在课堂中实现这些特殊功能,我们将考虑两个情况的示例,其中使用这些特殊功能会使我们的代码更清晰,更重要。

>

第一个示例是我想到的一些外部解决方案,因为在python中创建了一个简单的岩纸剪辑游戏,第二个本质上将有点数学上有点数学'm将带您浏览每行代码

一个简单的岩石剪刀游戏

>以防万一您不熟悉岩纸剪辑游戏,它最初是在两个人中玩的手游戏,其中涉及摇滚,纸或剪刀的迹象。了解游戏的整个历史并不重要,重要的是知道如何确定获胜者。在传统的环境中,岩石的手势总是会击败剪刀,但会输掉纸,剪刀的手势会击败纸,输给岩石,显然,纸会输给剪刀并击败摇滚。我们可以总结这一点,如下所示

>

image of rock, paper, scissors game对于我们对此游戏的python仿真,我们将仅将玩家数量限制在两个,一个玩家将是计算机,另一个玩家将是用户。另外,这不是机器学习文章或有关计算机视觉的文章,我们的用户仍然必须在终端上的岩石,纸张和剪刀之间输入选项才能工作。 在进行实际编码之前,我们可以退后一步并考虑希望我们的python脚本成为现实是很好的。为了解决这一挑战,我将使用随机模块启用计算机选择岩石,纸或剪刀的随机选项。为了实现我们的代码如何评估获胜者,我将做出以下假设:>


>我也要采取oop方法;我们的岩石,纸和剪刀将被视为对象而不是弦变量。与其为每个类别创建三个单独的类,不如创建一个可以代表其中任何一个的类。这种方法还可以让我向您展示特殊方法如何使生活更轻松。现在到一个有趣的方面!
类定义

>命名我们的rps听起来可能有些奇怪,但是我发现“ rps”这个名字是一个很好的合适,因为每个字母都来自缩写,r for rock,p for paper for paper,s for scissors。这里要注意的是,创建我们的班级实例需要两个参数:选择和名称。我们已经说过,脚本的用户必须在终端上输入其选定选项,而不是让我们的用户输入“纸张”(对他们来说可能是如此压力)输入“ p”(或“ p”)以选择“纸”,这就是选择的目的。名称属性是实际名称,例如“纸”。因此,现在我们知道了每个参数的目的,我们现在可以通过创建一个实例
来检查我们的类
>>> p = rps('p', 'paper') # create an instance
>>> p.name
# return : paper
>>> p.pick
# return : p
>>> print(p)
# return : <__main__.rps object at 0x...>
>创建了类实例并具有正确的属性,但请注意,当我们尝试打印持有类实例的变量内容时,我们得到了什么。在了解我们的类实例如何返回奇数字符串的技术细节之前,让我们通过添加单个特殊功能并查看差异来更新我们的类定义。


现在,让我们创建一个实例,然后尝试再次打印我们的类实例
>>> p = rps('p', 'paper')
>>> print(p)
# return : rps(p, paper)
正如我们所看到的,通过定义'
repen>'的方法,我们可以取得更好的结果。让我们对我们的班级定义进行一次更改。


现在,让我们创建一个实例并重新测试。

>>> p = rps('p', 'paper')
>>> p
# return : rps(p, paper)
>>> print(p)
# return : paper
>>> str(p)
# return : 'paper'
>>> repr(p)
# return : 'rps(p, paper)'
要知道这里发生了什么,我们需要了解打印功能。打印功能将所有非关键字参数(如我们的p变量)转换为使用内置python类str的字符串。如果在我们的变量上调用str()失败,则python会落在内置的reper函数上。当我们的对象上调用str时,它会寻找__str__方法,如果没有发现,则会失败,然后搜索__repr__方法。 __str__和__repr__方法都是用于字符串表示我们对象的特殊方法。 __repr__方法给出了我们对象的官方字符串表示,而__str__方法给出了我们对象的友好字符串表示。我通常会说__repr__方法就像与另一个开发人员交谈,通常显示如何打电话给我们的班级,而__str__就像与我们程序的用户交谈(如在这种情况下的播放器),您通常只想返回一个简单的字符串,例如“纸”,以向用户展示他选择的内容。>
>尽管我将__repr__和__st__表示为我们类定义中的两个特殊功能,但实际上有第三种特殊方法,是的,它是最常见的方法,即__init__函数。它用于初始化我们的课程,并在返回我们的类实例之前通过__new__ __ ________________。我是否只是提到了我们尚未定义的另一种特殊方法?是的,我做到了。您可能会感兴趣的是,python会自动为我们的班级添加其他一些特殊方法。您可以通过在我们的类实例上调用内置功能dir来检查它们,

>>> dir(p)
# returns : ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'pick']

特殊功能或方法可以通过命名的方式来识别,它们总是以双重下划线''开始,并以双重下划线'

'结束,因为这种特殊的命名这些方法,它们是通常称为daunder方法(双 下划线= dunder)。因此,如果方法名称始于双重下划线,那么它很可能是一种特殊的方法。为什么不确定呢?这仅仅是因为python并没有阻止我们使用dunder语法定义自己的方法。还可以回到我们的游戏脚本。

>现在留给我们的一切都是让我们让我们的脚本知道如何确定赢家。如前所述,我将使用比较来评估赢家。
>

# comparison logic
rock > scissors
scissors > paper
paper > rock

要实现此解决方案,我将添加一个字典并使用daunder_than方法。字典钥匙将是岩石,纸和剪刀的缩写。每个密钥的值将是钥匙可以击败的唯一另一个元素。>


请注意新的代码行,首先是选项字典,然后是__gt__方法定义。有了这些新的代码行,让我们看看我们的代码现在具有哪些新功能。

>

# create a rock instance
>>> r = rps('r', 'rock')

# create a paper instance
>>> p = rps('p', 'paper')

# create a scissors instance
>>> s = rps('s', 'scissors')

>>> print(r,p,s)
# return : rock paper scissors

>>> p > r # paper wins against rock
# return : true

>>> r > s # rock wins against scissors
# return : true

>>> s > p # scissors wins against paper
# return : true

>>> p < s # paper lose to scissors
# return : true

>>> p < r # paper lose to rock
# return : false

>>> p < s < r# paper lose to scissors which lose to rock
# return : true

>>> p >= r paper wins or tie to rock
# return : traceback (most recent call last): 
  file "<stdin>", line 1, in <module> 
typeerror: '>=' not supported between instances of 'rps' and 'rps'
>仅通过添加__gt__ __ __gt__,我们的课堂实例就获得了魔法(有时称为魔术方法,我们可以看到原因)。通过实现daunder gt方法,我们的类实例现在与>和<符号息息相关,而不是≥和≤符号。原因是<只是>的否定。 <的特殊方法是__lt __(self,x),它可能只是否定调用__gt __(self,x)。
对于≥符号,其特殊方法是__ge __(self,x),必须将其定义才能使我们的对象与≥符号合作。但是在这个程序中,我们可以没有它。
另一个缺失的部分是检查两个单独的纸张实例是否相等。>

>>> p1 = rps("p", "paper")
>>> p2 = rps("p", "paper")
>>> p3 = p1
>>> p1 == p2
# return : false

>>> p1 == p3
# return : true

>>> id(p1)
# return : 140197926465008

>>> id(p2)
# return : 140197925989440

>>> id(p3)
# return : 140197926465008

>>> id(p1) == id(p3)
# return : true

>>> id(p2) == id(p1)
# return false

>平等比较符号的默认操作是比较对象的id。 p1和p2是不同的类实例,恰好具有相同的属性,但其id有所不同,因此并不相等。当我们将变量分配给类实例时,我们将该变量点提高到实例的地址,这是我们对p3的观察到与p1相同的id。我们可以选择通过定义和实施自己的__eq __(自我,其他)方法来覆盖平等比较如何在我们的对象上起作用。但是对于此脚本,我将使用其选择属性比较两个实例。现在我们已经定义了我们的班级并知道了它的工作原理,现在我们准备看到python脚本的完整实现

将它们全部放在一起


>让我介绍您的代码。我们已经熟悉rps类定义。如果您还记得,我们的代码旨在允许计算机随机选择选择,这就是随机模块的目的。随机模块可提供选择功能,该功能允许从峰值对象中“随机”选择元素,例如python中的列表。在这种情况下,列表是option_list。由于我们的班级是与大写字母一起使用的,因此我们必须始终使用大写属性来初始化对象以进行选择属性。这就是为什么我们首先使用.upper()将用户的输入转换为上情况(第34行)的原因。我们的用户类型也有可能以“ q”为例,因此我们必须通过检查大写字符是否是option_list中有效选项的一部分来验证我们的用户输入。映射字典使我们能够在经过验证后将用户的输入快速转换为rps的相应实例。 estuatue_winner函数利用比较符号来确定获胜者。因为我们希望代码在循环中运行直到找到获胜者,所以我们使用一段时间循环,当找到获胜者时,evaluate_winner函数返回true,然后将打破循环并退出游戏。

这是运行代码的各种结果之一

>我们的python代码按预期运行,尽管可能需要添加一些改进或新功能。最重要的是,我们看到在班级定义中使用特殊方法如何给我们的代码带来更大的感觉。假设我们要采用不同的方法,例如使用嵌套if语句,我们的essuatue_winner方法看起来像这样的

>
def evaluate_winner(user_choice, comp_choice):
    # check if user choice is 'r'
    if user_choice == 'r':
        # check if comp_choice is 'r'
        if comp_choice == 'r':
            # it is a tie
            ...
        elif comp_choice == 's':
            # user wins
            ...
        else:
          # computer wins
          ...
    if ... 
     # do the same for when user_choice is 's' and then for
     # when user_choice is 'p'
>除冗长的代码外,这种方法的问题是,如果我们希望添加一个新元素,钻石可以击败岩石和剪刀,而不是纸(出于未知原因),我们的if语句将真正开始看起来很像尴尬的。而在我们的oop方法中,我们要做的就是修改符合
的选项

options = {"r" : ["s"], "p" : ["r"], "s" : ["p"], "d" : ["r", "s"]}
,然后我们将__gt__中的if语句更改为

>

def __gt__(self,x):    
    if x.pick in self.options[self.pick]:
        return true
    else:
        return false

我们可以使语句缩短image of running the python script

def __gt__(self, x):
   return True if x.pick in self.options[self.pick] else False

得出结论,您应该注意一些有关使用特殊方法的事情:>

>

您几乎没有(或从未)自己直接打电话给他们,让python为您打电话
>

>
定义使用dunder命名语法的函数时,您应该认为python有一天可以定义这样的功能并赋予其不同的含义。这可能会破坏您的代码或以意外的方式行事


>

>您当然不必实现所有特殊方法。只是一对您真的确定需要的夫妇。请记住,简单胜于复杂。如果有一种更简单的方法,则应使用该方法

>
  • 要了解有关python数据模型的更多信息,我建议官方的python文档。

    >我还建议阅读luciano ramalho

    的fluent python
  • >这是主题的第一部分,在下一部分中,我们将与操作员过载并使其使itable对象
  • >

    希望您喜欢这篇文章!

  • 好了,本文到此结束,带大家了解了《用Python数据模型编写Pythonic代码》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多文章知识!

声明:本文转载于:dev.to 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>