如何在Python中使用exec()-你需要知道的一切
作者:互联网
Python中的exec()
函数允许我们从字符串中执行Python代码块。当我们需要运行动态生成的Python代码时,Python中的这个内置函数可以派上用场,但由于附带的一些安全风险,建议不要粗心大意地使用它。
在本教程中,我们将学习
-
如何使用Python的
exec()
函数 -
如何使用带有代码示例的
exec()
函数执行Python代码 -
从字符串和Python源文件中执行Python代码
-
使用
globals
和locals
参数
Python的exec()函数
Python的exec()
函数允许我们执行任何一段Python代码,无论该代码是大是小。此功能帮助我们执行动态生成的代码。
想想一个Python解释器,它接受一段代码,在内部处理它,并执行它,exec()
函数也会这样做。它就像一个独立的Python解释器。
exec()
能够执行简单的Python程序以及功能齐全的Python程序。它可以执行函数调用和定义、类定义和实例化、导入等。
句法
exec(object [ , globals [ , locals]])
-
object
-它必须是字符串或代码对象。如果是字符串,则将其解析为一组Python语句,除非发生语法错误,否则将执行。如果它是一个代码对象,那么它将简单地执行。 -
globals
和locals
-这允许我们提供代表全局和本地命名空间的字典。
返回值
exec()函数的返回值为None。这可能是因为每段代码都没有最终结果。
初步看一眼
以下是exec()
函数工作的初步观察。
obj = ["apple", "cherry", "melon", "strawberry"]
code = "print([sliced[:4] for sliced in obj if 'a' not in sliced])"
exec(code)
.........
['cher', 'melo']
使用exec()
函数的另一个示例
# The code will continuously run and we can run our code as we do in
# Python interpreter
while True:
exec(input(">>> "))
>>> print("Welcome to GeekPython")
Welcome to GeekPython
>>> import numpy as np
>>> print(np.random.randint(16, size=(2, 4)))
[[11 13 3 13]
[ 7 6 15 5]]
>>> x = ["apple", "banana", "cherry", "mango"]
>>> print([fruit for fruit in x if "a" not in fruit])
['cherry']
它的工作原理与Python解释器完全相同,获取我们的代码,在内部处理它,执行它,并返回正确的结果。
我们正在运行一个无限循环,在里面,我们从命令行获取输入,并将其包装在exec()
函数中以执行它。
从字符串输入执行代码
我们可以使用exec()
函数来执行字符串格式的代码。我们可以使用多种方法来构建基于字符串的输入:
-
使用单行代码
-
使用新的行字符
-
使用具有正确格式的三引号字符串
使用基于单行字符串的输入
在Python中,单行代码,也称为单行代码,是写在一行中的代码,可以同时执行多个任务。
如果我们写一行Python代码,它看起来会是这样的:
obj = ["apple", "cherry", "melon", "strawberry"]
print([sliced[:4] for sliced in obj if 'a' not in sliced])
输出信息
['cher', 'melo']
但是,如果我们使用exec()
运行上述代码,代码将是
obj = ["apple", "cherry", "melon", "strawberry"]
exec("print([sliced[:4] for sliced in obj if 'a' not in sliced])")
#-----------------------------OR--------------------------------
exec("code = [sliced[:4] for sliced in obj if 'a' not in sliced]")
如果我们执行它,我们上面写的其他代码将不返回任何内容,相反,输出将存储在code
变量中,以便以后访问。
执行由新行字符分隔的多行代码
我们可以使用新的行字符\n
将多个语句组合在单行字符串中。
exec("square = int(input('Enter the number: '))\nprint(f'The square of {square}:', square**2)")
输出信息
Enter the number: 30
The square of 30: 900
定义了一个新的行字符(\n
),以使exec()
函数将我们基于单行字符串的代码理解为Python语句的多行集。
使用三引号字符串
在Python中,我们经常使用三引号来评论或记录我们的代码。然而,在这种情况下,我们将使用它来生成基于字符串的输入,其外观和行为与普通Python代码完全相同。
我们在三引号内编写的代码必须正确缩进和格式化,就像普通的Python代码一样。请参阅下面的示例,以更好地理解。
sample_code = """
integers = [4, 7, 2, 9, 44]
def square(num):
return num ** 2
def odd_num(num):
return num % 2 == 1
square_if_even = [square(number) for number in integers if number % 2 == 0]
odd_number = [number for number in integers if odd_num(number)]
print("Original values:", integers)
print("Square of even number:", square_if_even)
print("Odd number:", odd_number)
"""
exec(sample_code)
输出信息
Original values: [4, 7, 2, 9, 44]
Square of even number: [16, 4, 1936]
Odd number: [7, 9]
上述代码与标准Python代码类似,具有适当的缩进和格式,但它被包裹在三引号中,导致基于字符串的输入存储在sample_code
变量中,然后使用theexecexec()
函数执行。
从Python文件中执行代码
我们可以通过使用open()函数读取文件的内容,使用exec()
函数从Python(.py
)源文件中执行代码。
考虑以下示例,其中包括一个包含以下代码的sample.py
文件:
# sample.py
def anime(name):
print(f"Favourite anime: {name}")
anime("One Piece")
list_of_anime = input("Enter your favourite anime: ").split(",")
print("Your Favourite anime:", list_of_anime)
代码只需在这里打印动漫名称,而以下代码块则以逗号分隔您最喜欢的动漫的输入,并打印所需的输出。
使用exec函数执行Python源文件
with open("sample.py", mode="r") as sample:
file = sample.read()
exec(file)
输出信息
Favourite anime: One Piece
Enter your favourite anime: One Piece, Naruto, Demon Slayer, Jujutsu kaisen
Your Favourite anime: ['One Piece', ' Naruto', ' Demon Slayer', ' Jujutsu kaisen']
我们使用open()
函数,使用with
语句将.py
文件作为常规文本文件打开,然后使用文件对象上的.read()
将文件内容作为字符串读取到file
变量中,该变量在exec
中传递以执行代码。
使用全局和本地参数
这些参数是完全可选的。我们可以使用globals
和locals
参数来限制不需要的函数、方法和变量的使用。
由于这些参数是可选的,省略它们会导致exec()
函数在当前作用域中执行输入代码。考虑以下示例以更好地理解它。
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
exec(code)
输出信息
Hello
上述代码运行成功,并产生了一个结合了两个全局变量的输出。由于未指定全局和locals
参数,exec()
函数在当前范围内执行代码输入。在这种情况下,目前的范围是全球性的。
以下是如何在当前代码范围内获取变量值的示例。
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
print(out)
Traceback (most recent call last):
.....
NameError: name 'out' is not defined. Did you mean: 'oct'?
在前面的代码中,我们试图在调用exec()
之前访问out
变量的值,并收到一个错误。
但是,我们可以在调用exec()
后访问out变量的值,因为代码输入中定义的变量在调用exec()
后将在当前范围内可用。
exec(code)
print(out)
输出信息
Hello
Hello
使用globals
和locals
参数
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
exec(code, {"str1": str1})
输出信息
Traceback (most recent call last):
....
NameError: name 'str2' is not defined. Did you mean: 'str1'?
代码返回错误,因为我们没有定义字典中按住str2
的键,因此exec()
无法访问它。
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
exec(code, {"str1": str1, "str2": str2})
print(out)
输出信息
Hello
Traceback (most recent call last):
....
NameError: name 'out' is not defined. Did you mean: 'oct'?
exec()
函数现在可以访问两个全局变量,代码返回输出时没有错误,但这次我们在调用exec()
后无法访问out,因为我们使用自定义字典为exec()
提供执行范围。
这里有一个一起使用locals
和globals
的例子
code = """
out = str1 + str2 + " " + x
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
def local():
# local variable
x = "there"
exec(code, {"str1": str1, "str2": str2}, {"x": x})
local()
输出信息
Hello there
在上述代码中,我们从本地函数中调用exec()
。在全局范围内,我们在局部范围(函数级别)中有全局变量和一个局部变量。全局参数指定变量str1
和str2
,而局部参数指定变量x
。
阻止不必要的方法和变量
使用全局和locals
参数,我们可以控制是否在代码输入中限制或使用任何变量或方法。在本节中,我们将限制Pythondatetime
模块中的一些函数。
from datetime import *
code = """
curr_time = datetime.now()
print(curr_time)
"""
exec(code, {})
输出信息
Traceback (most recent call last):
....
NameError: name 'datetime' is not defined
我们从datetime
模块中限制了datetime
方法的使用,这导致了一个错误。
使用必要的方法和变量
我们只能使用exec()
所需的方法。
from datetime import *
# Allowing only two methods
allowed_param = {'datetime': datetime, 'timedelta': timedelta}
exec("print(datetime.now())", allowed_param)
exec("print(datetime.now() + timedelta(days=2))", allowed_param)
# date() method is not allowed
exec("print(date(2022, 9, 4))", allowed_param)
输出信息
2022-10-15 18:40:44.290550
2022-10-17 18:40:44.290550
Traceback (most recent call last):
....
NameError: name 'date' is not defined
发生错误是因为不允许使用date
方法。除了datetime
和timedelta
两种方法外,datetime
模块中的所有方法都是被禁止的。
让我们看看我们还能用globals
和locals
参数完成什么。
from datetime import *
# Setting globals parameter to __builtins__
globals_param = {'__builtins__': __builtins__}
# Setting locals parameter to take only print(), slice() and dir()
locals_param = {'print': print, 'dir': dir, 'slice': slice}
exec('print(slice(2))', globals_param, locals_param)
exec('print(dir())', globals_param, locals_param)
输出信息
slice(None, 2, None)
['dir', 'print', 'slice']
在exec()
函数中,只能执行sliceslice()
方法和所有内置方法。尽管sliceslice()
方法不是来自datetime
模块,但它在这里工作得很好。
我们还可以通过将其设置为None
来限制__builtins__
的使用。
from datetime import *
# Setting globals parameter to none
globals_param = {'__builtins__': None}
# Setting locals parameter to take only print(), slice(), sum() and dir()
locals_param = {'print': print, 'dir': dir, 'slice': slice, 'sum': sum}
# Allowed methods directory
exec('print(dir())', globals_param, locals_param)
exec('print(f"Sum of numbers: {sum([4, 6, 7])}")', globals_param, locals_param)
输出信息
['dir', 'print', 'slice', 'sum']
Sum of numbers: 17
我们限制了__builtins__
的使用,因此我们无法使用内置方法,只能在exec()
中执行print
、sum
、slice
和dir
方法。
结论
我们已经学会了如何使用内置的Python exec()
函数从基于字符串的输入执行代码。它允许您运行动态生成的Python代码。