如何为python自定义类进行类型转换

Pra*_*ain 5 python casting class python-2.7

考虑一堂课;

class temp:
    def __init__(self):
        pass
Run Code Online (Sandbox Code Playgroud)

我以它为对象;

obj = temp()
Run Code Online (Sandbox Code Playgroud)

将其转换为字符串;

strObj = str(obj)
Run Code Online (Sandbox Code Playgroud)

现在,如何将strObj转换为temp类的对象?

org = temp(strObj)
Run Code Online (Sandbox Code Playgroud)

exh*_*uma 7

要回答这个问题,一种方法是“滥用”__repr__eval(). 我们先来看看__repr__文档(重点:我的):

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

这通常用于调试,因此表示信息丰富且明确是很重要的。

考虑到这一点,我们知道建议返回一个__repr__可以与eval(). 值“应该看起来像一个有效的 Python 表达式”的语句暗示了这一点。

例子

这是一个使用它的示例。该示例也覆盖了__eq__,但只是为了方便打印输出。为了完整起见,我们还为实例添加了一个值。

该示例创建了一个新实例。然后将该值转换为字符串 using __repr__(通过使用该repr()函数。接下来将该字符串值传递给eval()它将评估字符串并返回结果。结果将是同一类的新实例并存储在 中second_instance。我们还打印出id()以可视化我们确实有两个不同的实例。最后我们证明first_instance == second_instance确实是True

class MyClass:

    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return isinstance(self, MyClass) and self.value == other.value

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)


first_instance = MyClass(123)
print('First instance: repr=%r, id=%d' % (first_instance, id(first_instance)))

stringified = repr(first_instance)
print('Stringified: %r' % stringified)

second_instance = eval(stringified)  # !!! DANGEROUS (see below) !!!
print('Second instance: repr=%r, id=%d' % (second_instance, id(second_instance)))

print('First == Second: %r' % (first_instance == second_instance))
Run Code Online (Sandbox Code Playgroud)

什么时候可以这样做?

如果绝对一切都在您的控制之下,这是100%可以接受的!这意味着:eval()

  • eval()被调用的范围在你的控制之下
  • 评估字符串中的任何地方都不应包含来自外部来源的数据。外部来源包括:
    • 数据库值
    • 用户输入
    • 从磁盘读取数据
    • ... 基本上任何 I/O

牢记所有这些并保证项目 I/O 在未来的任何时候都不会以eval()调用结束几乎是不可能的。因此,我强烈建议在重要的生产代码中避免这种情况,因为它会打开令人讨厌的安全漏洞。

对于不在生产环境中运行的代码,这是绝对可以接受的。例如单元测试、个人实用程序脚本等,但应始终考虑风险。

为什么这是危险的?

  • 传入的代码eval()在调用它的 Python 进程内执行,具有相同的权限。示例:您从多个用户可以访问的数据库中读取一个值,然后您eval()就可以使用它了。在这种情况下,另一个用户可能会通过数据库注入代码,并且该代码将以您的用户身份运行!
  • 使用eval()时,值来自外部来源开辟了代码注入的可能性。
  • 不保证repr()会返回有效的 Python 表达式。这只是文档的建议。因此,对evalwith的调用__repr__容易出现运行时错误。
  • 在上面的例子中,作用域调用eval()需要“知道”这个类MyClass(它必须被导入)。它只查找名称。因此,如果纯属偶然的情况下,作用域中存在相同的名称,但指向另一个对象,您将无意中调用其他东西,并可能遇到奇怪的错误。当然,这是一个边缘情况。

更安全的选择

使用许多可用的序列化选项之一。最流行、最简单的方法是将对象与 JSON 字符串相互转换。上面的例子可以像这样安全:

import json


class MyClass:

    @staticmethod
    def from_json(document):
        data = json.loads(document)
        instance = MyClass(data['value'])
        return instance

    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return isinstance(self, MyClass) and self.value == other.value

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)

    def to_json(self):
        data = {
            'value': self.value
        }
        return json.dumps(data)


first_instance = MyClass(123)
print('First instance: repr=%r, id=%d' % (first_instance, id(first_instance)))

stringified = first_instance.to_json()
print('Stringified: %r' % stringified)

second_instance = MyClass.from_json(stringified)
print('Second instance: repr=%r, id=%d' % (second_instance, id(second_instance)))

print('First == Second: %r' % (first_instance == second_instance))
Run Code Online (Sandbox Code Playgroud)

这只是稍微困难一点,但更安全。

相同的方法可以与其他序列化方法一起使用。流行的格式有:

  • XML
  • YAML
  • ini/cfg 文件
  • pickle(请注意,这使用字节而不是文本作为序列化介质)。
  • MessagePack(请注意,这使用字节而不是文本作为序列化介质)。
  • 自定义实现
  • ...


Bry*_*nta 6

对于那些谁正在寻找覆盖转换内置命令,例如int(obj)float(obj)str(obj),看到在Python超载INT() 。您需要在对象上实现__int____float____str__


Dem*_*cht 0

正如 Anand 在评论中指出的,您正在寻找的是对象序列化和反序列化。实现此目的的一种方法是通过 pickle(或 cPickle)模块:

>>> import pickle

>>> class Example():
...     def __init__(self, x):
...             self.x = x
...
>>> a = Example('foo')
>>> astr = pickle.dumps(a) # (i__main__\nExample\np0\n(dp1\nS'x'\np2\nS'foo'\np3\nsb.
>>> b = pickle.loads(astr)
>>> b
<__main__.Example instance at 0x101c89ea8>
>>> b.x
'foo'
Run Code Online (Sandbox Code Playgroud)

但请注意,使用 pickle 模块的一个问题是处理实现版本。正如 Python 文档中所建议的,如果您希望未腌制的实例自动处理实现版本控制,您可能需要添加版本实例属性并添加自定义 __setstate__ 实现:https: //docs.python.org/2/library/pickle .html#对象。设置状态。否则,无论对对象本身进行的代码更改如何,序列化时对象的版本将与反序列化时获得的版本完全相同。