【问题标题】:Can the default implementation of __repr__ be improved based on the __init__ function?可以基于 __init__ 函数改进 __repr__ 的默认实现吗?
【发布时间】:2019-06-06 13:13:57
【问题描述】:

据我了解,__repr__ 用于表示对象的开发者/解释者友好表示,并且应该是有效的 Python 代码,当传递给 eval() 时会重新创建相同的对象。

来自 Python 文档:

object.repr(自我)

由 repr() 内置函数和字符串转换(反引号)调用,以计算对象的“官方”字符串表示。如果可能的话,这应该看起来像一个有效的 Python 表达式,可用于重新创建具有相同值的对象(给定适当的环境)。如果这不可能,则应返回 <...> 形式的字符串。返回值必须是字符串对象。如果一个类定义了 repr() 而不是 str(),那么当实例的“非正式”字符串表示时也使用 repr()该类是必需的。

链接:https://docs.python.org/2/reference/datamodel.html#object.repr

例如:

class tie(object):
    def __init__(self, color):
        self.color = color
t = tie('green')
repr(t) # prints <tie object at 0x10fdc4c10>

# can the default implementation be improved to tie(color='green')
# based on the parameters passed in the __init__ function

除了向后兼容性/现有行为之外,更改该实施会面临哪些挑战?

【问题讨论】:

  • 我不确定这里的问题是什么...你是问是否可以更改类的__repr__ dunder 方法? - 是的,它在调试或读取日志/异常回溯时起到了有用的作用,使用 eval() 的语句不正确,但如果对象的 repr 具有对象的所有状态信息,它会很有帮助 - 除非它们的状态很大它会淹没日志的任何其他信息,例如
  • @DerteTrdelnik 我也有点困惑......我想他们在问是什么阻止了python开发人员提供__repr__()的不同默认实现(例如)基于签名班级的__init__()
  • 问题是为什么 python 解释器提供默认实现为&lt;tie object at 0x10fdc4c10&gt;,而不是更多地工作来生成它可以的tie(color='green')。是否有任何警告,因为通过查看__init__ 的参数生成表示将是完全错误的?
  • @glibdud - 正是我的观点
  • 哦,好的,它存在重大问题-我将尝试在答案中总结一些

标签: python


【解决方案1】:

这基本上就是pickle 试图做的事情。这个想法是,由于内存中的对象是一个图,如果您重新访问该图,您可以重建该对象。

Pickle 会生成一系列指令来重构数据,但原则上它可以直接生成 Python 代码。

虽然我将考虑它的所有问题,但 Pickling 大量用于分布式应用程序,其中系统需要将数据从一个进程传输到另一个进程。

当您希望能够通过复制和粘贴到提示符中来重建对象时,__repr__ 方法在调试中非常方便。

让我们看看酸洗在哪里不起作用:

  1. 有些对象实际上代表对象!例如,网络套接字标识管理实际网络连接的内核资源。即使您可以重新创建这些,也不会有另一台机器在监听。

  2. 某些对象具有现实世界的后果。一个对象可能代表一个不应离开进程内存的密码或其他秘密,例如通过打印到日志中。

  3. 要确定首先构造哪些对象,您必须执行topological sort,虽然存在快速算法,但它们不是免费的。特别是,如果在发生这种情况时另一个线程修改了图表,事情会变得很糟糕。

  4. 如果您保存此数据并尝试在另一个版本上重新加载它,它将失败。如果您设计新版本来接受旧数据,则开发速度会变得缓慢,因为您的代码变得更加扭曲以处理向后兼容性。

  5. 它与您的对象和 Python 的对象模型紧密耦合,因此您最终需要重新实现它。

  6. 它可以访问 Python 中的任何内容,因此如果有人传入shutil.rmtree('/'),机器将忠实地执行它。

其中大部分都不是显示障碍,但要修复需要权衡取舍。内置__repr__ 做的这么少的主要原因是为了确保简单的事情保持简单。

attrs 这样的模块启发了python 3.7 中的dataclasses 模块,提供了一个罐装的__repr__,它在很大程度上满足了您的要求并回答了我认为您正在想象的大多数用例.

我正在研究一种可以解决其中一些问题的语言,我总结了some of the different existing approaches 来处理我上面提到的问题。

【讨论】:

    【解决方案2】:

    根据对象的创建方式设置默认 repr 会导致效率低下和混乱

    尺寸

    __init__ 参数必须通过副本存储在对象的某处 - 使对象膨胀

    并非所有对象都简单地将这些值复制到它们自己中

    例如:

    class GreedyMan:
        def __init__(self, coins):
            self.most_valuable_coin = max(coins)
    

    你必须在这里保存整个硬币收藏

    类是可变的

    使用 0xff00ff 初始化的 Color 类可以在其生命周期内更改为另一种颜色

    class Color:
        def __init__(self, color):
            self.color = color
    
        def dilute(self, factor):
            self.color = self.color * factor
    

    dilute 可以更改类的状态,因此您将不再拥有 0xff00ff 的颜色,而是其他颜色,并且如果出现异常,例如“我不接受非红色 - 提供颜色('红色') " 程序员需要进行一些调试,直到他注意到有人使用 dilute 来获得一些奇怪的阴影..

    那么为什么不打印整个状态 - 所有类属性

    结果可能是巨大的/无限的

    图可以有包含其他节点甚至循环的节点

    class ChainPart:
        def __init__(self, parent):
            self.parent = parent
            self.children = []
    
        def add_child(self, child):
            self.children.append(child)
    
    a=ChainPart(None)
    b=ChainPart(a)
    b.add_child(a)
    

    链部分b 当递归打印出所有内容时,必须打印a 部分,该部分将打印部分b ... 等等无限

    因此,解决这些问题最明显的方法是让 repr 保持简单,并允许程序员使用对象类中的自定义 __repr__ 方法对其进行更改。

    【讨论】:

    • 当我提出这个问题时,我有点短视,我对 init 的想象仅限于构造函数只会接收对象的属性作为参数和参数名称将保持与对象属性名称相同。我现在想对我自己的问题投反对票。 >_
    • 存在这样的对象 - named tuples ,它们的 repr 是您所描述的方式,因为它们是不可变的并且仅作为容器工作 - 它们不会更改初始化时使用的参数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    • 2015-05-19
    相关资源
    最近更新 更多