【问题标题】:How write an efficient overload function of __dict__?如何编写一个高效的__dict__重载函数?
【发布时间】:2019-10-04 01:12:18
【问题描述】:

我想实现一个to_dict 函数,它的行为类似于内置的__dict__ 属性,但允许我拥有自定义逻辑。 (用于构建pandas DataFrame,见下例)

但是我发现我的 to_dict 函数比 __dict__ 慢约 25% 即使他们做同样的事情。如何改进我的代码?

class Foo:
    def __init__(self, a,b,c,d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

    def to_dict(self):
        return {
            'a':self.a,
            'b':self.b,
            'c':self.c,
            'd':self.d,
        }

list_test = [Foo(i,i,i,i)for i in range(100000)]

%%timeit
pd.DataFrame(t.to_dict() for t in list_test)
# Output: 199 ms ± 4.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
pd.DataFrame(t.__dict__ for t in list_test)
# Output: 156 ms ± 948 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

这个问题离题了,但与我的最终目标有关:从自定义对象列表构造 pandas DataFrame 的最有效方法是什么?我目前的做法取自https://stackoverflow.com/a/54975755/1087924

【问题讨论】:

  • 他们做的事情并不完全相同。 to_dict 每次都会创建一个新对象,而 t.__dict__ 已经存在。
  • 在创建Foo 的实例时生成dict 对象,并在值更新时更新它。然后在to_dict 上,您返回对象,而不是在每次调用时生成它。应该更快。
  • 注意,__dict__ 不是函数,它只是一个包含该对象的命名空间的属性。

标签: python pandas performance class


【解决方案1】:

__dict__ 不会将对象“转换”为dict(与__int____str__ 等不同),它是存储对象(可写)属性的位置。

我认为您的实施相当有效。考虑这个简化的例子:

import dis

class Foo:
    def __init__(self, a):
        self.a = a
    def to_dict(self):
        return {'a': self.a}

foo = Foo(1)

dis.dis(foo.to_dict)
dis.dis('foo.__dict__')

我们可以看到 Python 每次都会查找属性并创建一个新的 dict(另外,您需要调用 .to_dict,此处未显示):

  7           0 LOAD_CONST               1 ('a')
              2 LOAD_FAST                0 (self)
              4 LOAD_ATTR                0 (a)
              6 BUILD_MAP                1
              8 RETURN_VALUE

虽然访问现有属性要简单得多:

  1           0 LOAD_NAME                0 (foo)
              2 LOAD_ATTR                1 (__dict__)
              4 RETURN_VALUE

但是,您可以将自定义表示存储在实例上,获得与 __dict__ 相同的精确字节码,但是您需要在对 Foo 的所有更改上正确更新它(这将花费一些速度和内存)。如果在您的用例中更新不常见,这可能是一个可以接受的权衡。

在您的示例中,一个简单的选项是覆盖 __getattribute__,但我猜 Foo 具有其他属性,因此使用 setter 可能会更方便:

class Foo:
    def __init__(self, a):
        self.dict = {}
        self.a = a

    @property
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self._a = value
        self.dict['a'] = value

foo = Foo(1)
print(foo.dict)  # {'a': 1}
foo.a = 10
print(foo.dict)  # {'a': 10}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-24
    • 2011-04-11
    • 2010-09-21
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多