9
votes

Pourquoi l'argument de mot clé de la définition de la classe peut-il accepter une appelable?


0 commentaires

3 Réponses :


2
votes

Eh bien, le type est bien sûr mymetaclass . METACLASS_CALLABLE est initialement "sélectionné" comme métaclass depuis Il a été spécifié dans la métaclass kwarg et comme tel, c'est __ appel __ (un appel de fonction simple) va être effectué.

Il arrive que l'appelant imprimer puis appelez mymetaclass .__ appel __ (quel type __ depuis __ call n'a pas été remplacé par mymetaclass ). l'affectation de CLS .__ classe __ est faite à myMetaclass .

métaclass_callable est appelé une fois, puis semble être irrécupérable

Oui, il est seulement initialement invoqué, puis les mains contrôlent à mymetaclass . Je ne suis au courant d'aucun attribut de classe qui conserve cette information.

Les classes dérivées n'utilisent pas (autant que je puisse dire) métaclass_callable de quelque manière que ce soit.

Nope, si aucun métaclass est explicitement défini, La meilleure correspondance des métaclasses de bases (ICI myClass ) sera utilisé (résultant dans mymetaclass ).


Quant à la question 2 , tout ce que vous pouvez faire avec une appelable est également possible en utilisant une instance de type avec __ appel __ remplacé en conséquence. Quant à pourquoi , vous ne voudrez peut-être pas aller à la pleine création de classe soufflée si vous souhaitez simplement apporter des modifications mineures lors de la création d'une classe.


1 commentaires

Des deux choix que j'ai donnés, quelle est votre réponse à la questionner une? Quant à votre réponse à la question 2, pouvez-vous donner un exemple dans lequel l'utilisation d'une appelable est en quelque sorte "moins pleine soufflée" que de dériver de type ?



7
votes

En ce qui concerne votre première question, la métaclasse doit être MyMetaclass Code> (ce que c'est ainsi):

# Provide a PEP 3115 compliant mechanism for class creation
def new_class(name, bases=(), kwds=None, exec_body=None):
    """Create a class object dynamically using the appropriate metaclass."""
    meta, ns, kwds = prepare_class(name, bases, kwds)
    if exec_body is not None:
        exec_body(ns)
    return meta(name, bases, ns, **kwds)

def prepare_class(name, bases=(), kwds=None):
    """Call the __prepare__ method of the appropriate metaclass.

    Returns (metaclass, namespace, kwds) as a 3-tuple

    *metaclass* is the appropriate metaclass
    *namespace* is the prepared class namespace
    *kwds* is an updated copy of the passed in kwds argument with any
    'metaclass' entry removed. If no kwds argument is passed in, this will
    be an empty dict.
    """
    if kwds is None:
        kwds = {}
    else:
        kwds = dict(kwds) # Don't alter the provided mapping
    if 'metaclass' in kwds:
        meta = kwds.pop('metaclass')
    else:
        if bases:
            meta = type(bases[0])
        else:
            meta = type
    if isinstance(meta, type):
        # when meta is a type, we first determine the most-derived metaclass
        # instead of invoking the initial candidate directly
        meta = _calculate_meta(meta, bases)
    if hasattr(meta, '__prepare__'):
        ns = meta.__prepare__(name, bases, **kwds)
    else:
        ns = {}
    return meta, ns, kwds


def _calculate_meta(meta, bases):
    """Calculate the most derived metaclass."""
    winner = meta
    for base in bases:
        base_meta = type(base)
        if issubclass(winner, base_meta):
            continue
        if issubclass(base_meta, winner):
            winner = base_meta
            continue
        # else:
        raise TypeError("metaclass conflict: "
                        "the metaclass of a derived class "
                        "must be a (non-strict) subclass "
                        "of the metaclasses of all its bases")
    return winner


12 commentaires

Pour la question 2, je ne comprends pas comment vous répondez à ma question: "Y a-t-il quelque chose que vous puissiez faire avec un appelable que vous ne pouvez pas faire avec une instance de type type ? Quel est le but d'accepter une appelable arbitraire? "


@Neilg Comme je l'ai mis à jour, il n'y a pas de but particulier, c'est juste la nature des Maclasses.


Cela semble suggérer que vous pouvez contourner _calculate_meta , mais je n'ai pas pu faire cela.


@Neilg Yase c'est pour Python 3. Si vous contournez que vous n'obtiendrez pas de méta appropriée car il est dit dans Doc.


Je veux dire, je n'ai pas pu le contourner en spécifiant une métaclasse sans type. (Je déclenche toujours l'erreur appropriée).


Utilisation d'un appelable: Classe autretreuse (myClass, métaclass = autre_metaclass_callable): Pass


@Neilg C'est une chose différente, je pense que vous avez terminé avec un METACLASS Confit , c'est à ce sujet, la métaclasse de votre classe dérivée n'est pas une sous-classe des métaclasses de toutes ses bases. Comme le dernier trace de la trace.


Mais qui est coché _calculate_meta , qui doit être ignoré lorsque méta n'est pas une instance de type (une fonction régulière n'est pas une instance de Tapez ).


Je l'ai compris. C'est parce que quelque chose s'est passé en cours de route et d'objets / typeObject.c: Type_new ne coïncide plus avec lib / type.py: new_class. La version Python appelle conditionnellement _Calculate_meta alors que la version C appelle cela inconditionnellement. Je considère que la mise en œuvre C est la "version correcte".


@Neilg Dans ce cas, il exécutera la fonction et que vous appelez une classe à l'intérieur de cette fonction (avec la même métaclasse que l'une des classes de base), il augmentera un conflit .


@Neilg Je ne sais pas si cela exécute le code C avant le code Python ici. Comme je l'ai dit, cela pourrait être parce que après avoir appelé la classe dans la fonction.


Il ne gère pas du tout le code Python. Cela appelle simplement la mise en œuvre C, qui est incompatible.



2
votes

Concernant la question 1, je pense que la "métaclasse" d'une classe CLS code> doit être comprise comme type (CLS) code>. Ce mode de compréhension est compatible avec le message d'erreur de Python dans l'exemple suivant:

>>> class MetaMeta(type):
...     def __call__(mcls, name, bases, methods):
...         metabases = set(type(X) for X in bases)
...         metabases.add(mcls)
...         if len(metabases) > 1:
...             mcls = type(''.join([X.__name__ for X in metabases]), tuple(metabases), {})
...         return mcls.__new__(mcls, name, bases, methods)
... 
>>> class Meta1(type):
...     __metaclass__ = MetaMeta
... 
>>> class Meta2(type):
...     __metaclass__ = MetaMeta
... 
>>> class C1:
...     __metaclass__ = Meta1
... 
>>> class C2:
...     __metaclass__ = Meta2
... 
>>> type(C1)
<class '__main__.Meta1'>
>>> type(C2)
<class '__main__.Meta2'>
>>> class C3(C1,C2): pass
... 
>>> type(C3)
<class '__main__.Meta1Meta2'>


0 commentaires