En la mayoría de las publicaciones, la gente suele decir que type es una función incorporada si se le proporciona un argumento, y es una metaclase si se le proporcionan 3 argumentos.

Pero en documento de Python, la firma de {{ X0}} es:

class type(object)
class type(name, bases, dict)

Entonces, ¿significa que type es una clase incorporada y no una función incorporada, incluso si se proporciona un argumento?

3
Myrfy 30 oct. 2017 a las 04:09

3 respuestas

La mejor respuesta

type se llama "metaclase" porque es una clase que produce otras clases (tipos AKA). Se comporta como una clase normal. En particular, tiene el equivalente de un método __new__ que se vería así en Python:

class type(object):

    def __new__(cls, *args):
        num_args = len(args)

        if num_args not in (1, 3):
            raise TypeError('type() takes 1 or 3 arguments')

        # type(x)
        if num_args == 1:
            return args[0].__class__

        # type(name, bases, dict)
        name, bases, attributes = args
        bases = bases or (object,)

        class Type(*bases):
            pass

        Type.__name__ = name

        qualpath = Type.__qualname__.rsplit('.', 1)[0]
        Type.__qualname__ = '.'.join((qualpath, name))

        for name, value in attributes.items():
            setattr(Type, name, value)

        return Type

Class = type('Class', (), {'i': 1})
instance = Class()

print(type(instance))  # -> Class
print(instance.__class__)  # -> Class
print(type(type(instance)))  # -> type
print(Class.i)  # -> 1
print(instance.i)  # -> 1

Tenga en cuenta que al crear instancias de una clase, el valor de la nueva instancia es lo que se devuelve de __new__. En el caso de type, __new__ siempre devuelve un objeto de tipo (clase AKA). Aquí hay un ejemplo de una clase que extiende int para usar -1 como valor predeterminado en lugar de 0:

def Int__new__(cls, *args):
    if not args:
        return cls(-1)
    return super(cls, cls).__new__(cls, *args)

Int = type('Int', (int,), {'__new__': Int__new__})

i = Int()
print(type(i))  # -> Int
print(i.__class__)  # -> Int
print(type(type(i)))  # -> type
print(i)  # -> -1

j = Int(1)
print(j)  # -> 1

Para investigar realmente cómo funciona type, eche un vistazo a el código C en type_new. Puede ver allí (desplazarse unas pocas líneas hacia abajo) que type(x) es un caso especial que devuelve inmediatamente el tipo (clase AKA) de x. Cuando haces type(name, bases, dict), se invoca la maquinaria de creación de tipos.

Para más diversión, prueba lo siguiente:

type(object)
type(type)
isinstance(object, object)
isinstance(type, object)
type(1)
type(type(1))
2
30 oct. 2017 a las 19:47

El type incorporado siempre devuelve un tipo de objeto , que es básicamente una clase. Eso es cierto ya sea la forma de un argumento o la forma de tres argumentos. En el caso de un argumento, el objeto devuelto es el tipo (clase) del argumento. En el caso de tres argumentos, el objeto devuelto es un nuevo objeto de tipo (clase). En ambos casos, el objeto devuelto se puede usar para crear instancias de nuevos objetos (instancias de la clase).

Algunas clases se pueden usar como metaclases. Ese es un caso de uso común para la forma de tres argumentos de type. Pero hay otras formas de crear una clase que se puede usar como una metaclase, y otros usos para la forma de tres argumentos de type.

No es muy diferente de int, que es una función incorporada que devuelve un objeto de tipo int. int también es el nombre de una clase, y se puede usar para crear nuevos objetos:

>>> x = int()
>>> x
0
>>> type(x)
<class 'int'>

Y también como tipo, tiene más de una forma:

>>> y = int("A", 16)
>>> y
10
1
Paul Cornelius 30 oct. 2017 a las 03:38

La distinción entre clases y funciones en Python no es tan marcada como en otros lenguajes.

Puede usar la sintaxis de llamada de función para invocar una función real, pero también una instancia de una clase con el método __call__, e incluso una clase que anula __new__ puede comportarse como una función.

La palabra que Python usa para todos estos es invocable : un objeto que puede invocar usando la sintaxis de llamada (foo()), que se evalúa como resultado.

El type incorporado es un callable. Se puede llamar.

A veces tratas a type como una función, y funciona, por lo que puedes decir que es una función. Otras veces lo tratas como una clase, por lo que puedes decir que es una clase. La implementación real no debería importar.

Esto es escribir en un pato en acción: si camina como un pato, y grazna como un pato, entonces debe ser un pato.

0
slezica 30 oct. 2017 a las 01:17