ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2023-07-30 16:31:10  阅读:305  来源: 互联网

标签:Python 函数 变量


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

在本教程中,我们将学习

  • 如何使用Python的exec()函数

  • 如何使用带有代码示例的exec()函数执行Python代码

  • 从字符串和Python源文件中执行Python代码

  • 使用globalslocals参数

Python的exec()函数

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

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

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

句法

exec(object [ , globals [ , locals]])
  • object-它必须是字符串或代码对象。如果是字符串,则将其解析为一组Python语句,除非发生语法错误,否则将执行。如果它是一个代码对象,那么它将简单地执行。

  • globalslocals-这允许我们提供代表全局和本地命名空间的字典。

返回值

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,函数,变量
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有