Python2与Python3的语法区别引起了我对exec的注意,进而探讨一下python的命名空间(作用域)
exec()函数
exec()最有用的地方在于可以动态地创建代码字符串。然而,如果字符串是从其他地方获得的——很有可能是用户——那么几乎不能确定其中到底包含什么代码。所以为了安全起见,可以增加一个字典,起到命名空间的作用。
例:
# Python2版:
from math import sqrt
scope = {}
exec 'sqrt = 1' in scope
print(sqrt(4))
print(scope['sqrt'])
#Python3版:
from math import sqrt
scope = {}
exec('sqrt = 1', scope)
print(sqrt(4))
print(scope['sqrt'])
#输出为
2.0
1
可以看到,潜在的破坏性代码并不会覆盖sqrt函数,原来的函数能正常工作,而通过exec赋值的变量sqrt只在它的作用域内有效。
这时如果将scope打印出来的话,会看到其中包含很多东西,
因为內建的_builtins_字典自动包含所有的內建函数和值。
{'__builtins__':
{'__name__': 'builtins',
'__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.",
'__package__': '',
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>),
'__build_class__': <built-in function __build_class__>,
'__import__': <built-in function __import__>,
'abs': <built-in function abs>,
'all': <built-in function all>,
'any': <built-in function any>,
'ascii': <built-in function ascii>,
'bin': <built-in function bin>,
'callable': <built-in function callable>,
'chr': <built-in function chr>,
'compile': <built-in function compile>,
'delattr': <built-in function delattr>,
'dir': <built-in function dir>,
'divmod': <built-in function divmod>,
'eval': <built-in function eval>,
'exec': <built-in function exec>,
'format': <built-in function format>,
'getattr': <built-in function getattr>,
'globals': <built-in function globals>,
'hasattr': <built-in function hasattr>,
'hash': <built-in function hash>,
'hex': <built-in function hex>,
'id': <built-in function id>,
'input': <built-in function input>,
'isinstance': <built-in function isinstance>,
'issubclass': <built-in function issubclass>,
'iter': <built-in function iter>,
'len': <built-in function len>,
'locals': <built-in function locals>,
'max': <built-in function max>,
'min': <built-in function min>,
'next': <built-in function next>,
'oct': <built-in function oct>,
'ord': <built-in function ord>,
'pow': <built-in function pow>,
'print': <built-in function print>,
'repr': <built-in function repr>,
'round': <built-in function round>,
'setattr': <built-in function setattr>,
'sorted': <built-in function sorted>,
'sum': <built-in function sum>,
'vars': <built-in function vars>,
'None': None, 'Ellipsis': Ellipsis,
'NotImplemented': NotImplemented,
'False': False,
'True': True,
'bool': <class 'bool'>,
'memoryview': <class 'memoryview'>,
'bytearray': <class 'bytearray'>,
'bytes': <class 'bytes'>,
'classmethod': <class 'classmethod'>,
'complex': <class 'complex'>,
'dict': <class 'dict'>,
'enumerate': <class 'enumerate'>,
'filter': <class 'filter'>,
'float': <class 'float'>,
'frozenset': <class 'frozenset'>,
'property': <class 'property'>,
'int': <class 'int'>,
'list': <class 'list'>,
'object': <class 'object'>,
'reversed': <class 'reversed'>,
'set': <class 'set'>,
'slice': <class 'slice'>,
'staticmethod': <class 'staticmethod'>,
'str': <class 'str'>,
'super': <class 'super'>,
'tuple': <class 'tuple'>,
'type': <class 'type'>,
'zip': <class 'zip'>,
'__debug__': True,
'BaseException': <class 'BaseException'>,
'Exception': <class 'Exception'>,
'TypeError': <class 'TypeError'>,
'StopAsyncIteration': <class 'StopAsyncIteration'>,
'StopIteration': <class 'StopIteration'>,
'GeneratorExit': <class 'GeneratorExit'>,
'SystemExit': <class 'SystemExit'>,
'KeyboardInterrupt': <class 'KeyboardInterrupt'>,
'ImportError': <class 'ImportError'>,
'ModuleNotFoundError': <class 'ModuleNotFoundError'>,
'OSError': <class 'OSError'>,
'EnvironmentError': <class 'OSError'>,
'IOError': <class 'OSError'>,
'EOFError': <class 'EOFError'>,
'RuntimeError': <class 'RuntimeError'>,
'RecursionError': <class 'RecursionError'>,
'NotImplementedError': <class 'NotImplementedError'>,
'NameError': <class 'NameError'>,
'UnboundLocalError': <class 'UnboundLocalError'>,
'AttributeError': <class 'AttributeError'>,
'SyntaxError': <class 'SyntaxError'>,
'IndentationError': <class 'IndentationError'>,
'TabError': <class 'TabError'>,
'LookupError': <class 'LookupError'>,
'IndexError': <class 'IndexError'>,
'KeyError': <class 'KeyError'>,
'ValueError': <class 'ValueError'>,
'UnicodeError': <class 'UnicodeError'>,
'UnicodeEncodeError': <class 'UnicodeEncodeError'>,
'UnicodeDecodeError': <class 'UnicodeDecodeError'>,
'UnicodeTranslateError': <class 'UnicodeTranslateError'>,
'AssertionError': <class 'AssertionError'>,
'ArithmeticError': <class 'ArithmeticError'>,
'FloatingPointError': <class 'FloatingPointError'>,
'OverflowError': <class 'OverflowError'>,
'ZeroDivisionError': <class 'ZeroDivisionError'>,
'SystemError': <class 'SystemError'>,
'ReferenceError': <class 'ReferenceError'>,
'BufferError': <class 'BufferError'>,
'MemoryError': <class 'MemoryError'>,
'Warning': <class 'Warning'>,
'UserWarning': <class 'UserWarning'>,
'DeprecationWarning': <class 'DeprecationWarning'>,
'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>,
'SyntaxWarning': <class 'SyntaxWarning'>,
'RuntimeWarning': <class 'RuntimeWarning'>,
'FutureWarning': <class 'FutureWarning'>,
'ImportWarning': <class 'ImportWarning'>,
'UnicodeWarning': <class 'UnicodeWarning'>,
'BytesWarning': <class 'BytesWarning'>,
'ResourceWarning': <class 'ResourceWarning'>,
'ConnectionError': <class 'ConnectionError'>,
'BlockingIOError': <class 'BlockingIOError'>,
'BrokenPipeError': <class 'BrokenPipeError'>,
'ChildProcessError': <class 'ChildProcessError'>,
'ConnectionAbortedError': <class 'ConnectionAbortedError'>,
'ConnectionRefusedError': <class 'ConnectionRefusedError'>,
'ConnectionResetError': <class 'ConnectionResetError'>,
'FileExistsError': <class 'FileExistsError'>,
'FileNotFoundError': <class 'FileNotFoundError'>,
'IsADirectoryError': <class 'IsADirectoryError'>,
'NotADirectoryError': <class 'NotADirectoryError'>,
'InterruptedError': <class 'InterruptedError'>,
'PermissionError': <class 'PermissionError'>,
'ProcessLookupError': <class 'ProcessLookupError'>,
'TimeoutError': <class 'TimeoutError'>,
'open': <built-in function open>,
'quit': Use quit() or Ctrl-D (i.e. EOF) to exit,
'exit': Use exit() or Ctrl-D (i.e. EOF) to exit,
'copyright': Copyright (c) 2001-2018 Python Software Foundation.
All Rights Reserved.
Copyright (c) 2000 BeOpen.com.
All Rights Reserved.
Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.
Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.
},
'sqrt': 1}
eval()函数
eval(用于“求值”)是类似于exec的內建函数。exec会执行一系列Python语句,而eval会计算Python表达式(以字符串形式书写),并且返回结果值。(exec并不返回任何对象。)
例:
# 利用eval创建一个Python计算器
print(eval(input("Enter an arithmetic expression: ")))
#输入:
1 + 2
#输出:
3
跟exec一样,eval也可以使用命名空间。尽管表达式几乎不像语句那样为变量重新赋值。(事实上,可以给eval提供两个命名空间,一个全局的一个局部的。全局的必须是字典,局部的可以是任何形式的映射。)
尽管表达式一般不给变量重新赋值,但它们的确可以(比如可以调用函数给全局变量重新赋值)。所以使用eval对付一些不可信任的代码并不比exec安全。
关于作用域的例子:
#给exec或者eval提供命名空间时,还可以在真正使用命令空间前放置一些值进去:
scope = {}
scope['x'] = 2
scope['y'] = 3
print(eval('x * y', scope))
#输出:
6
#exec或者eval调用的作用域也能在另外一个上面使用:
scope = {}
exec('x = 2', scope)
print(eval('x * x', scope))
#输出:
4