python - pd.DataFrame(...) 在它之前定义元类时导致 TypeError
问题描述
我一直在玩元类来尝试对它们有一个好的感觉。我想出的一个非常简单(且毫无意义)的方法如下:
class MappingMeta(type, collections.abc.Mapping):
def __setattr__(self, *args, **kwargs):
raise RuntimeError("Can not set attributes of Mapping type")
def __call__(self, *args, **kwargs):
raise RuntimeError("Can not directly instantiate Mapping type")
def __getitem__(self, value):
return getattr(self, value)
def __iter__(self):
return (k for k in vars(self) if not k.startswith("_"))
def __len__(self):
return sum(1 for _ in self)
class Mapping(metaclass=MappingMeta):
pass
class Test(Mapping):
x = 1
y = 2
该课程在孤立的情况下完美运行。
现在当我做类似的事情时:
import pandas as pd
class MappingMeta(type, collections.abc.Mapping):
... # same as above
class Mapping(metaclass=MappingMeta):
pass
class Test(Mapping):
x = 1
y = 2
print(pd.DataFrame({'x': [1, 2]}))
我收到以下错误:
Traceback (most recent call last):
File "metamapping.py", line 22, in <module>
print(pd.DataFrame({"x": [1, 2]}))
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/core/frame.py", line 803, in __repr__
self.to_string(
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/core/frame.py", line 939, in to_string
return fmt.DataFrameRenderer(formatter).to_string(
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/io/formats/format.py", line 1031, in to_string
string = string_formatter.to_string()
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/io/formats/string.py", line 23, in to_string
text = self._get_string_representation()
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/io/formats/string.py", line 38, in _get_string_representation
strcols = self._get_strcols()
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/io/formats/string.py", line 29, in _get_strcols
strcols = self.fmt.get_strcols()
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/io/formats/format.py", line 519, in get_strcols
strcols = self._get_strcols_without_index()
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/site-packages/pandas/io/formats/format.py", line 752, in _get_strcols_without_index
if not is_list_like(self.header) and not self.header:
File "pandas/_libs/lib.pyx", line 1033, in pandas._libs.lib.is_list_like
File "pandas/_libs/lib.pyx", line 1038, in pandas._libs.lib.c_is_list_like
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/abc.py", line 98, in __instancecheck__
return _abc_instancecheck(cls, instance)
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/abc.py", line 102, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/abc.py", line 102, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
File "/Users/rnlondono/miniconda3/envs/p38/lib/python3.8/abc.py", line 102, in __subclasscheck__
return _abc_subclasscheck(cls, subclass)
[Previous line repeated 1 more time]
TypeError: descriptor '__subclasses__' of 'type' object needs an argument
奇怪的是(至少对我来说)如果我print(pd.DataFrame({'x': [1, 2]}))
在元类的定义之前放另一个,整个事情就可以了。出于某种原因,它必须是印刷品……
import pandas as pd
print(pd.DataFrame({'x': [1, 2]}))
class MappingMeta(type, collections.abc.Mapping):
... # same as above
class Mapping(metaclass=MappingMeta):
pass
class Test(Mapping):
x = 1
y = 2
print(pd.DataFrame({'x': [1, 2]}))
因此,作为一个 hacky 解决方案,我绝对可以使用它......
另外,当我将其collections.abc.Mapping
作为超类删除时,MappingMeta
我没有收到错误消息-但是我没有得到我正在寻找的功能(基本上Test
用作字典)
我知道这可能不是元类的最佳用途,但我只是好奇是否有人对正在发生的事情有任何想法。
编辑
接受的响应回答了这个问题,但只是为了提供一些关于我为什么在元类中使用 collections.abc.Mapping 类的上下文。我这样写的原因是我可以编写如下类:
class Test(Mapping):
x = 1
y = 2
z = 3
'x' in Test # True
list(Test.items()) # [('x', 1), ('y', 2), ('z', 3)]
{**Test} # {'x': 1, 'y': 2, 'z': 3}
虽然接受的答案肯定回答了这个问题,但我最终决定只实施提供的方法collections.abc.Mapping
以避免任何其他潜在的冲突
解决方案
问题是其中的抽象基类collections.abc
并非旨在用作元类。
尽管这可能是“可以想象的”,但需要确切地知道他在做什么:元类用于模板类本身的创建,而 collections.abc 是基类- 不是元类,提供了一个框架来实现公共集合工作量最小的模式。
因此,显然在实例化 的实例时collections.abc.mapping
,Python 机器会对所有已注册的映射类型进行一些检查,而您的 quimera 会妨碍您,并使事情中断。
干净的解决方案只是手动在您的元类上实现您想要的任何映射方法。即使您解决了这个问题 - 就像@DS_London 的答案一样,Mapping mixin 实现了许多可能与type
自身需要的机制发生冲突的方法和机制 - 而且您无法控制。你得到的错误就是这样一个例子。
collections.abc 将免费为您提供的只是 __contains__, keys, items, values, get, __eq__, __ne__
-您甚至可能不需要所有这些-所以只需实现您想要的任何东西,然后完成。
Dataframe
如果在定义元类之前实例化并不会中断,原因很简单:在此之前,用于虚拟子类检查的 collections.abc 机制还没有被元类毒害。
推荐阅读
- c++ - 从向量中获取字符串流中的值后不会重置
- javascript - 如何从位于身份验证层后面的 API 获得响应?
- mathjax - 使用 Mathjax 对齐方程
- java - 而那些不是员工的人 - 我如何确保它不会在 executeQuery 中出错?
- laravel - 如何使用ckeditor解决laravel错误405
- r - 使用存储的反应值重命名添加到对象的值
- angular - 防止 Angular 6 中的分层依赖注入
- c++ - Pthread 创建失败
- c# - 从 github repo 构建 SDK
- indexing - 检索从 CGAL 的 surface_neighbor_coordinates_3 返回的最近表面点的索引