【问题标题】:Printing individual list objects within __str__ using .format(**self.__dict__)使用 .format(**self.__dict__) 在 __str__ 中打印单个列表对象
【发布时间】:2012-11-15 18:52:18
【问题描述】:

我正在打印游戏角色对象的统计数据块。在上一个问题中,我展示了一种在 __str__ 函数中使用显示对象数据的方法,如下所示:

def __str__(self):
    if self.poisoned is True:
        status = "[POISONED]"
    else:
        status = ""
    self.status = status
    return ('NAME: {name} {status}\n' \
            'XP:   {xp}\n' \
            'HP:   {hit_points}\n' \
            'SP:   {spell_points}\n' \
            'STR:  {strength}\n' \
            'DEX:  {dexterity}\n' \
            'WEAPON: {weapon}\n' \
            'SPELL:  {spell}\n' \
            'ITEM:   {item}\n' \
            'AURA:   {aura}\n' \
            ).format(**self.__dict__)

我要解决的问题与 WEAPON、SPELL、ITEM 和 AURA 变量有关。这些项目在 Character 对象中定义为单个项目列表:weapon=[] 等等。使用上述方法返回列表对象而不是它包含的不带[] 的对象。如果存在而不是[],我会看到一个空白的" " 字符串或列表包含的对象。

NAME: Bones 
XP:   0
HP:   100
SP:   100
STR:  14
DEX:  19
WEAPON:   []
SPELL:    []
ITEM:     []
AURA:     []

我尝试了许多实验,包括在定义 current_weapon = weapon[0] 后将 {weapon} 引用替换为 {current_weapon},如果列表对象为空,这将不起作用。这只是IndexError: list index out of range 的错误。我可以在对象实例化时生成项目,但这不起作用,因为 self.item 有时会是一个空列表容器。

我可以使用 " " 对象传播列表,但随后必须将它们与替换项目一起处理并跟踪这似乎非常不雅且可能很麻烦。

我似乎无法以一种优雅的方式来打印上面__str__ 返回当前设计的列表对象。我仍在学习 Python,并且我希望有一个简单的添加可以附加到这个 return 字符串来做到这一点。

【问题讨论】:

  • 不需要用于续行的反斜杠。
  • 谢谢。没有它们,代码看起来好多了。
  • 如果列表包含不止一件事,您希望发生什么?
  • 我不打算让列表包含超过一个项目。 WEAPON、SPELL 和 AURA 将始终包含一个对象,但 ITEM 将包含一个与其他 3 一样的字符串项目,或者它将是空的(例如在消耗药水时)。所以我在 @987654337 中使用的 return 方法@ 应该显示不带括号的项目,如果列表中不存在任何项目供参考,则显示空白。
  • 你想通过使用 1 项列表而不是直接持有该项目来完成什么?如果你需要一些“可选”的东西,你可以使用None来表示玩家没有武器等。

标签: python string function python-3.x string-formatting


【解决方案1】:

您可以创建一个本地副本,然后修改您想要的值,然后将其传递给格式:

def __str__(self):
    local_data = self.__dict__.copy()

    local_data['status'] = "[POISONED]" if self.poisoned else ""

    local_data['weapon'] = " " if not self.weapon else ','.join(self.weapon)

    return ('NAME: {name} {status}\n' \
            'XP:   {xp}\n' \
            'HP:   {hit_points}\n' \
            'SP:   {spell_points}\n' \
            'STR:  {strength}\n' \
            'DEX:  {dexterity}\n' \
            'WEAPON: {weapon}\n' \
            'SPELL:  {spell}\n' \
            'ITEM:   {item}\n' \
            'AURA:   {aura}\n' \
            ).format(**local_data)

这样做可能比修改简单的格式属性更好,就像你对self.status 所做的那样。现在您只是在修改临时副本。

【讨论】:

  • 不会,因为创建的副本是 __str__ 函数的本地副本,并且每次都会重新创建。
  • 正确。 str 方法每次都会被重新评估,因此它反映了当前状态。本地副本被垃圾回收
  • 我也将状态部分清理为oneliner
  • 你特别想要这样的东西,因为在转换方法 (__str__) 中修改对象状态 (self.status) 是代码异味。
  • 如果你试图让武器成为不同的类型,你需要考虑到这一点。通常最好只使用一种类型,这样您就不会在任何时候造成混淆。
【解决方案2】:

你可以用一种简单的方式做到这一点,即使不是那么微不足道。您可以修改字符串格式以获取整个对象并利用属性的力量。这样做的好处是不创建字典副本,这对于大对象来说可能很昂贵。

我会给你一个应该接近你需要的例子:

class A(object):
    def __init__(self):
        # one full list and one empty one
        self.c = [1,2,3]
        self.d = []

    #these two preperties create a string versione when requeste
    c_str = property(lambda self: ", ".join(str(i) for i in self.c))
    d_str = property(lambda self: ", ".join(str(i) for i in self.d))

    def __str__(self):
        #you have to use the dotted version because properties are not visibles 
        # from the dict attribute
        string = "c = {0.c_str} \nd = {0.d_str}"
        return string.format(self)
a = A()
print str(a)
# c = 1, 2, 3 
# d = 

如果您正在编写某种游戏属性,则可能是一个巨大的救星,因为您可以使用它们来获取复杂的值作为属性而不是函数,从而创建更简洁的代码。它们允许您实现甚至检查值的插入,例如值是否为正。

编辑:

为什么我使用0.c_str 而不是c_str?这是因为属性是特殊对象,只有当您使用点符号 (self.c_str) 访问它们时才会调用它们。它们不存在于对象 __dict__ 中,因此您无法使用它。如果您尝试打印 __dict__,您将只看到值 cd

这就是为什么我将整个对象传递给格式函数并访问其属性而不是传递对象字典。

如果您不喜欢 0.c_str 表示法,您可以以不同的方式对其进行转义,例如使其接近通常的表示法:

"{self.c_str}".format(self=self)

"{foo.c_str}".format(foo=self)

【讨论】:

  • 肯定有一些你展示的东西我还没有学过,但我想我主要看到你在这里做什么。在您的string = 中,我可以看到{0.c_str} 在生成的代码中完成了什么,但为什么0.c_str 而不仅仅是c_str。我想更好地理解为什么会这样。
  • 谢谢,这确实有助于我更好地理解您的方法。如果我理解正确的话,0self 以这种方式使用时相同。对吗?
  • 即链接到格式化函数。 0 的意思是“拿走我传递给你的第一个对象”,而在该字符串中使用 self 的意思是“拿走我传递给 self 的对象”。它只是与您将对象传递给格式函数的方式有关(匿名 Vs 命名参数)
【解决方案3】:

另一种选择是使用字符串格式化的强大功能来检查传入的属性,以及 self 是方法中的局部变量这一事实:

def __str__(self):
    status = '[POISONED]' if self.poisoned else ''
    weapon = self.weapon[0] if self.weapon else ''
    spell = self.spell[0] if self.spell else ''
    item = self.item[0] if self.item else ''
    aura = self.aura[0] if self.aura else ''
    return ('NAME: {self.name} {status}\n'
            'XP:   {self.xp}\n'
            'HP:   {self.hit_points}\n'
            'SP:   {self.spell_points}\n'
            'STR:  {self.strength}\n'
            'DEX:  {self.dexterity}\n'
            'WEAPON: {weapon}\n'
            'SPELL:  {spell}\n'
            'ITEM:   {item}\n'
            'AURA:   {aura}\n'
            ).format(**locals())

【讨论】:

  • 在实际代码中,我可能会将其与@EnricoGiampieri 的技术结合起来,但使用@property 装饰器来创建属性。
  • 这个技巧很巧妙,我喜欢。为了清楚起见,locals 函数返回一个字典,其中包含函数范围内定义的所有变量。我只认为在@Vin Breau 心中你可以拥有多种武器、法术和排序,所以也许字符串连接会是更好的列表索引。
  • 我喜欢这个,光看就明白了。 Enrico 的方法更高级一些,我对它不像 jdi 的方法那么舒服。不过,Enrico's 需要重新阅读和使用,以确保我完全理解它。
猜你喜欢
  • 1970-01-01
  • 2011-02-07
  • 2011-04-03
  • 2019-05-08
  • 2023-03-23
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多