编程语言
首页 > 编程语言> > 如何在Python中使用exec()-你需要知道的一切

如何在Python中使用exec()-你需要知道的一切

作者:互联网

Python中的exec()函数允许我们从字符串中执行Python代码块。当我们需要运行动态生成的Python代码时,Python中的这个内置函数可以派上用场,但由于附带的一些安全风险,建议不要粗心大意地使用它。

在本教程中,我们将学习

Python的exec()函数

Python的exec()函数允许我们执行任何一段Python代码,无论该代码是大是小。此功能帮助我们执行动态生成的代码。

想想一个Python解释器,它接受一段代码,在内部处理它,并执行它,exec()函数也会这样做。它就像一个独立的Python解释器。

exec()能够执行简单的Python程序以及功能齐全的Python程序。它可以执行函数调用和定义、类定义和实例化、导入等。

句法

exec(object [ , 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中传递以执行代码。

使用全局和本地参数

这些参数是完全可选的。我们可以使用globalslocals参数来限制不需要的函数、方法和变量的使用。

由于这些参数是可选的,省略它们会导致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

使用globalslocals参数

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()提供执行范围。

这里有一个一起使用localsglobals的例子

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()。在全局范围内,我们在局部范围(函数级别)中有全局变量和一个局部变量。全局参数指定变量str1str2,而局部参数指定变量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方法。除了datetimetimedelta两种方法外,datetime模块中的所有方法都是被禁止的。

让我们看看我们还能用globalslocals参数完成什么。

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()中执行printsumslicedir方法。

结论

我们已经学会了如何使用内置的Python exec()函数从基于字符串的输入执行代码。它允许您运行动态生成的Python代码。

标签:Python,函数,变量
来源: