我想实现一个 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
最佳答案
__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}
关于python - 如何编写一个高效的__dict__重载函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56173947/