This issue tracker has been migrated to GitHub ,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2014年07月04日 20:45 by abusalimov, last changed 2022年04月11日 14:58 by admin.
| Messages (6) | |||
|---|---|---|---|
| msg222312 - (view) | Author: Eldar Abusalimov (abusalimov) * | Date: 2014年07月04日 20:45 | |
When a new class is constructed Python checks for possible metaclass conflicts within bases and an explicitly specified one, if any, choosing the best available (the most specialized) one. That is the following implication is expected: issubclass(B, A) => issubclass(type(B), type(A)) However, changing __bases__ attribute can break this invariant silently without an error. >>> class O(object): ... pass ... >>> class M(type): ... pass ... >>> class N(type): ... pass ... >>> class A(O, metaclass=M): ... pass ... >>> class B(O, metaclass=N): ... pass ... >>> B.__bases__ = (A,) >>> B.__mro__ (<class '__main__.B'>, <class '__main__.A'>, <class '__main__.O'>, <class 'object'>) >>> type(B) <class '__main__.N'> >>> type(A) <class '__main__.M'> >>> issubclass(B, A) True >>> issubclass(type(B), type(A)) False Trying to derive from B now makes things look pretty weird: >>> class C(A, metaclass=N): ... pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases >>> class D(B, A): pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases >>> class E(B, metaclass=N): ... pass ... >>> type(E) <class '__main__.N'> That is one can extend a class but not its base (and not a class along its base). This effectively allows to bypass metaclass checks (by introducing a dummy class with the default metaclass, deriving it from a desired class with an inappropriate metaclass by changing __bases__ and using it instead of the desired class). This behavior is observed in 2.7, 3.2 and 3.4. I would expect the same check for metaclass conflicts when changing __bases__ as upon creating a new class: >>> # EXPECTED: ... >>> B.__bases__ = (A,) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases |
|||
| msg236753 - (view) | Author: Mark Lawrence (BreamoreBoy) * | Date: 2015年02月27日 14:47 | |
@Eldar sorry that this issue slipped our net. |
|||
| msg237072 - (view) | Author: Eldar Abusalimov (abusalimov) * | Date: 2015年03月02日 18:24 | |
@Mark, that OK, for the issue without a patch. :) I could make a patch, but I'm not sure whether the proposed behavior is right. It could be considered arguable, I guess... |
|||
| msg326532 - (view) | Author: Karthikeyan Singaravelan (xtreak) * (Python committer) | Date: 2018年09月27日 08:10 | |
Thanks for the report and patience. This behavior is still reproducible on master as of f55c64c632 . I am adding Raymond as part of triaging who might have a better explanation about this. Raymond, feel free to remove yourself if this is not relevant. A slightly cleaned up version of the program with repl statements removed for reference : class O(object): pass class M(type): pass class N(type): pass class A(O, metaclass=M): pass class B(O, metaclass=N): pass print(B.__bases__) print(B.__mro__) print(type(B)) print(type(A)) print(issubclass(type(B), type(A))) class C(A, metaclass=N): pass class D(B, A): pass class E(B, metaclass=N): pass |
|||
| msg326650 - (view) | Author: Raymond Hettinger (rhettinger) * (Python committer) | Date: 2018年09月28日 18:17 | |
> I am adding Raymond as part of triaging who might have a better explanation about this. Guido, is this something we care about? There is value in checking for metaclass conflicts when a class is created (to catch plausible mistakes and to assure that the right __new__() and __init__() methods run). But post-creation, it's unclear whether there is value in rerunning checks and whether its even possible to know how other base classes might have affected the class creation. Do this go in the "consenting adults" category (in the same way that we allow the __class__ attribute to be changed on instances without trying to verify whether it makes sense)? |
|||
| msg326651 - (view) | Author: Guido van Rossum (gvanrossum) * (Python committer) | Date: 2018年09月28日 18:34 | |
As long as you can't crash CPython with this, this is the responsibility of the code that assigns to __bases__. If they don't take care, the program's behavior is undefined. |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022年04月11日 14:58:05 | admin | set | github: 66118 |
| 2018年09月28日 18:34:40 | gvanrossum | set | messages: + msg326651 |
| 2018年09月28日 18:17:36 | rhettinger | set | versions:
+ Python 3.8, - Python 2.7, Python 3.4, Python 3.5 nosy: + gvanrossum messages: + msg326650 assignee: gvanrossum |
| 2018年09月27日 08:10:55 | xtreak | set | nosy:
+ rhettinger messages: + msg326532 |
| 2018年09月23日 08:50:57 | BreamoreBoy | set | nosy:
- BreamoreBoy |
| 2018年09月23日 06:08:18 | xtreak | set | nosy:
+ xtreak |
| 2015年03月02日 18:24:58 | abusalimov | set | messages: + msg237072 |
| 2015年02月27日 14:47:26 | BreamoreBoy | set | nosy:
+ BreamoreBoy messages: + msg236753 versions: + Python 3.5, - Python 3.2 |
| 2014年07月04日 20:45:25 | abusalimov | create | |