admin管理员组文章数量:1417720
Given the following Python code:
import ctypes
from collections.abc import Mapping
class StructureMeta(type(ctypes.Structure), type(Mapping)):
pass
class Structure(ctypes.Structure, Mapping, metaclass=StructureMeta):
pass
struct = Structure()
assert isinstance(struct, ctypes.Structure)
assert isinstance(struct, Mapping)
The metaclass is needed to avoid a metaclass conflict when deriving from both ctypes.Structure (metaclass _ctypes.PyCStructType
) and Mapping (metaclass abc.ABCMeta
).
This works fine when executed with Python 3.11. Alas, pylint 3.3.4 reports two errors:
test.py:5:0: E0240: Inconsistent method resolution order for class 'StructureMeta' (inconsistent-mro)
test.py:9:0: E1139: Invalid metaclass 'StructureMeta' used (invalid-metaclass)
How do I need to change the meta class to fix the error reported by pylint? Is it even a problem?
Given the following Python code:
import ctypes
from collections.abc import Mapping
class StructureMeta(type(ctypes.Structure), type(Mapping)):
pass
class Structure(ctypes.Structure, Mapping, metaclass=StructureMeta):
pass
struct = Structure()
assert isinstance(struct, ctypes.Structure)
assert isinstance(struct, Mapping)
The metaclass is needed to avoid a metaclass conflict when deriving from both ctypes.Structure (metaclass _ctypes.PyCStructType
) and Mapping (metaclass abc.ABCMeta
).
This works fine when executed with Python 3.11. Alas, pylint 3.3.4 reports two errors:
test.py:5:0: E0240: Inconsistent method resolution order for class 'StructureMeta' (inconsistent-mro)
test.py:9:0: E1139: Invalid metaclass 'StructureMeta' used (invalid-metaclass)
How do I need to change the meta class to fix the error reported by pylint? Is it even a problem?
Share Improve this question edited Jan 31 at 13:31 Markus asked Jan 31 at 9:25 MarkusMarkus 3,3972 gold badges26 silver badges37 bronze badges 3 |1 Answer
Reset to default 1As you can attest, there is no "error" in this code.
Working with metaclasses is hard - and having to create a compatible metaclass in cases like this shows some of the side effects.
The problem is most of what metaclasses do are things that take effect as the code is executed (i.e. in runtime) - while analysis tools like pylint, pyright, mypy, all try to see Python as if it was a static language, and as they evolve, they incorporate patterns to understand ever a little bit more of the language's dynamism.
You simply reached a step that pylint (or whatever tool) haven't reached yet - the plain workaround is just to ignore its fake error message (by adding a # noQA or equivalent comment in the infringing line).
Do it without ignoring the the linting error
- No, seriously, don't do this! The copying of the
Mapping
class in this way is subject to unforeseen side effects
But, for the sake of entertainment and completeness, this is the path to avoid the linter-error (involving much more experimental code practices and subject to surprise side effects than just silencing the QA tool):
A workaround there might be not to use the Mapping base class as it is, since it has a conflicting metaclass - it is possible for your resulting class to be registered as a Mapping (by using the Mapping.register
call as a decorator) - so that isinstance(struct, Mapping)
will still be true.
That would imply, of course, in reimplementing all the goodness collections.abc.Mapping
have, in wiring up all of a mapping logic so that one just have to implement a few methods.
Instead of that, we might just create a "non-abstract" version of Mapping by copying all of its methods over to another, plain, class - and use that to the multiple-inheritance with ctypes.Struct
from collections import abc
import ctypes
ConcreteMapping = type("ConcreteMapping", (), {key: getattr(abc.Mapping, key) for key in dir(abc.Mapping)})
@abc.Mapping.register
class Struct(ctypes.Structure, ConcreteMapping):
pass
s = Struct()
assert isinstance(s, abc.Mapping)
assert isinstance(s, ctypes.Structure)
(Needless to say, this throws away the only gain of abstractclasses in Python: the RuntimeError at the point one attempts to instantiate an incomplete "Mapping" class, without implementing one of the required methods as per the docs.)
Keep it simple: No Linter Error, no metaclass and no frankenstein clonning
Actually, there is a path forward, without potential side effects like either combining metaclasses (that, although working, may yield problems as Python versions change due to subtle modifications in how these metaclasses interact) - and without clonning collections.abc.Mapping
as above - if you are willing to let go of the most of the benefficts of inheriting from Mapping, namely, getting the .get
, __contains__
, values
, keys
, items
, __ne__
, __eq__
methods for free:
Just implement the methods your codebase will be actually using from Mapping (which might be just __getitem__
, in this case), and
register your class as a subclass of collections.abc.Mapping
: the isinstance check will return True. No side effects - just that if one attempts to use one of the non-existing methods for your class, it will raise a RuntimeError:
@collections.abc.Mapping.register
class Struct(ctypes.Structure):
_fields_ = ...
def __getitem__(self, key):
...
本文标签:
版权声明:本文标题:python - How to fix inconsistent method resolution order when deriving from ctypes.Structure and Mapping - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745272187a2650959.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
. If I only uses the metaclass, both asserts fails. – Markus Commented Jan 31 at 10:00