The built-in keyword type means a function or a cl

2019-05-11 22:22发布

问题:

In most posts, people often say type is a built-in function if it is provided with one argument, and it is a metaclass if provided with 3 arguments.

But in python's doc, the signature of type is:

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

So, does it means type is a built-in class and not a built-in function even if it is provided with one argument?

回答1:

type is called a "metaclass" because it's a class that produces other classes (AKA types). It behaves like a normal class. In particular, it has the equivalent of a __new__ method that would look something like this in 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

Note that when instantiating a class, the value of the new instance is whatever is returned from __new__. In the case of type, __new__ always returns a type object (AKA class). Here's an example of a class that extends int to use -1 as the default value instead of 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

To really dig into how type works, take a look at the C code in type_new. You can see there (scroll down a few lines) that type(x) is a special case that immediately returns the type (AKA class) of x. When you do type(name, bases, dict), the type creation machinery is invoked.

For more fun, try the following:

type(object)
type(type)
isinstance(object, object)
isinstance(type, object)
type(1)
type(type(1))


回答2:

The built-in type always returns a type object, which is basically a class. That's true whether it's the one-argument form or the three-argument form. In the one-argument case, the returned object is the type (class) of the argument. In the three argument case, the returned object is a new type object (class). In both cases the returned object can be used to instantiate new objects (instances of the class).

Some classes can be used as metaclasses. That's a common use case for the three-argument form of type. But there are other ways to make a class that can be used as a metaclass, and other uses for the three-argument form of type.

It's not that much different from int, which is a built-in function that returns an object of type int. int is also the name of a class, and can be used to create new objects:

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

And also like type, it has more than one form:

>>> y = int("A", 16)
>>> y
10


回答3:

The distinction between classes and functions in Python is not as stark as in other languages.

You can use function call syntax to invoke an actual function, but also an instance of a class with the __call__ method, and even a class that overrides __new__ can behave like a function.

The word Python uses for all these is callable: an object you can invoke using the call syntax (foo()), which evaluates to a result.

The type built-in is a callable. It can be called.

Sometimes you treat type like a function, and it works, so you can say it's a function. Other times you treat it like a class, so you can say it's a class. The actual implementation should not matter.

This is duck typing in action: if it walks like a duck, and it quacks like a duck, then it must be a duck.