【问题标题】:How does Python interpreter look for types?Python 解释器如何查找类型?
【发布时间】:2014-08-18 12:13:30
【问题描述】:

如果我这样写:

>>> a = float()

Python 解释器如何知道在哪里查找类型“float”?

我知道 'float' 是在 Lib/types.py 中定义的变量,指的是内置类型 types.FloatType。但是解释器如何为脚本构建所有可能类型的完整列表(包括用户定义和导入模块定义)?它在哪些地方看?我该怎么做才能在 Python 脚本中构建这样的列表?

【问题讨论】:

  • 试试:dir(__builtins__), help(__builtins__)
  • 是的,不是这样 :) 我需要一个完整的列表,这些函数只返回当前模块中定义的内容。所以我可以在 builtins 中拥有我的内容 + 里面的内容,但这些还不是全部。如果我说,导入模块 some_module 并在里面定义了一个类 SomeClass,它不会出现在列表中,而将它用作 some_module.SomeClass 不会有问题。我想得到口译员能得到的一切。
  • 完整列表是什么意思?什么的?你用这个列表做什么?
  • 您可以使用globals() 获取所有全局变量的列表,并使用locals() 获取所有局部变量的列表...我不知道您要做什么,但我的怀疑是你试图以一种非常尴尬的方式解决一个不同的问题......也许你应该提供更多细节是你真正想要做的事情。
  • @NedBatchelder 我需要一个可以在模块中使用的所有类型(和老式类)的完整列表。我正在编写一个检查函数参数类型的装饰器。所以我需要一个所有类型的列表。

标签: python


【解决方案1】:

您的问题似乎是,“这是一个类型声明吗?”答案是,它不是类型声明。 Python 中的名称没有与之关联的类型。名称指代值,而值具有在运行时确定的类型。

当 Python 执行 a = float() 时,它会查找名称 float,并在内置函数中找到它,它是一个函数。它不带参数调用该函数。返回值是一个浮点对象。然后使用名称a 来引用该对象。这就是它所做的一切。在执行这行代码之前,Python 不知道a 会变成什么,也不知道会涉及到浮点数。

Python 是动态的,因此您的代码行可能已经在此程序中:

def float():
    return "I'm not a float!"

a = float()

现在当a = float()被执行时,内置函数与它无关,任何地方都没有浮点数,a指的是一个字符串。

有关名称和值的更多信息,请参阅Facts and Myths about Python Names and Values

【讨论】:

    【解决方案2】:

    如果要将名称解析为全局名称,并且没有匹配的全局名称(您的模块中没有名为 float 的函数),那么 Python 会查看作为其一部分的 __builtins__ 对象每个模块的全局命名空间。

    该对象是一个内置模块,用 C 定义。请参阅 Python/bltinmodule.c

    float的情况下,名称绑定到CPyFloat_Type结构,在series of assignments中,PyFloat_Type结构在Objects/floatobject.c中定义

    从根本上说,Python 没有“类型声明”; floatstr等,只是符合一定行为的C结构;它们产生的值在查询时 Python 可以识别为某种类型。因此,float() 生成的任何内容都指向 float 作为其类型。

    而且因为__builtins__ 仅在您没有同名全局时才被咨询,您可以轻松地提供float() 可调用的自己的 实现;调用时返回完全不同的东西。

    如果您想枚举所有内置类型对象,请遍历内置对象并枚举任何 type() 实例:

    import __builtin__
    
    for name, obj in vars(__builtin__).iteritems():
        if isinstance(obj, type) and not issubclass(obj, BaseException):
            print name, obj
    

    __builtins__ 的工作原理是 CPython 实现的一个实现细节,但与此处使用的 __builtin__ module 公开的对象相同。

    请注意,您永远不能希望收集代码可以生成的所有可能类型。函数可以生成函数本地的新类型,并且在模块全局变量中列出;无需在此处列出类型即可工作:

    def dynamic_types(name, base=object):
        class DynamicType(base):
            def __repr__(self):
                '<{} (DynamicType) at {:#x}>'.format(self.__name__, id(self))
        DynamicType.__name__ = name
        return DynamicType()
    

    上面的函数每次调用它时都会生成具有 new 类型的对象(Python 类也是类型)。您不会在模块全局变量中找到该类型,也不会在内置函数中找到该类型。

    【讨论】:

    • 好的。在那之后?如果名称不在内置程序中,我的意思是。它是否递归地查看导入的模块?如果它不存在,抛出异常?还是去别的地方看看?
    • @Gleb:你在建什么?听起来您正在尝试实现 Python 解释器! :)
    • @Gleb:导入的模块是全局的。如果名称既不在您的全局变量中也不在 __builtins__ 中,则引发 NameError
    【解决方案3】:

    在您的问题中,您写道“解释器如何构建所有可能类型的完整列表”-但这包含错误的假设。没有可能的类型的完整列表。有一组有效的现有类型,但它们没有排列在列表中。

    您有一个命名空间,它通常按特定顺序排列:函数(本地)、模块(全局)、内置。在解析代码时,编译器还为literals 选择类型构造函数。每个地方的每个名字都可以指代任何对象,其中有很多类型:

    >>> [name for name in dir(__builtins__) 
         if isinstance(getattr(__builtins__,name), type)]
    ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 
     'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 
     'EnvironmentError', 'Exception', 'FloatingPointError', 'FutureWarning', 
     'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 
     'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 
     'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 
     'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 
     'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 
     'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 
     'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 
     'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', 'basestring', 
     'bool', 'buffer', 'bytearray', 'bytes', 'classmethod', 'complex', 'dict', 
     'enumerate', 'file', 'float', 'frozenset', 'int', 'list', 'long', 'memoryview', 
     'object', 'property', 'reversed', 'set', 'slice', 'staticmethod', 'str', 'super', 
     'tuple', 'type', 'unicode', 'xrange']
    

    解释器实际上并不关心有多少类型,只关心它们各自如何应用于解释要运行的代码。它通过使用每个对象都有的类型指针来发现:

    >>> type(1)
    <type 'int'>
    

    结果是,如果两种类型都不合适,他们的工作就是告诉你:

    >>> 1+"foo"
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    >>> "foo"+1
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: cannot concatenate 'str' and 'int' objects
    >>>
    

    第一个+实际上是int.__add__,第二个是str.__add__,只是通过左边的对象查找。 (像__radd__ 这样的替代尝试会变得有点复杂,但基本原理是一样的。)

    【讨论】:

      猜你喜欢
      • 2021-05-01
      • 2016-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多