【问题标题】:Python use decorator to implement context managerPython使用装饰器实现上下文管理器
【发布时间】:2019-08-27 06:27:07
【问题描述】:

我正在尝试制作两个带参数的装饰器。 First 创建一个带有元素 xlist 并调用 funcsecond 只是通过从 dict 传递参数来调用 first

def first(x=1):
    def wrapped(func):
        l = [x]
        func(l)
        print(l)
    return wrapped

def second(d={'x':10}):
    return first(x=d['x'])

third 函数只是修改传入的列表。 我想通过简单地调用third() 来使以下四个装饰器中的任何一个成为可能。我应该如何修改我的代码?

##@second
##@second({'x':100})
##@first
##@first(x=10)
def third(l):
    l.append(-1)

third()

例如:

## With @first,
## I am expecting to get [1, -1].
## With @first(x=10),
## I am expecting to get [10, -1].
## With @second,
## I am expecting to get [10, -1].
## With @second({x:100}),
## I am expecting to get [100, -1].

上面的代码是我的问题的抽象。我真正的问题是我想要一个为我处理打开和关闭连接的装饰器,这样我只需要编写处理连接的代码。

而连接需要参数,即first。我希望以不同的方式传递参数,即secondthird 是我要处理的连接。我希望 third 像普通函数一样被调用,它还使用装饰器处理打开和关闭连接。对不起,如果不应该这样使用装饰器,但我真的很想练习使用它。

---更新---

我想要实现的基本如下:

def context_manager(username='user', password='password'):
    conn = OpenConnection()
    func(conn)
    CloseConnection()

def context_manager2(d={'username': 'user', 'password': 'password'}):
    content_manager(username=d['username'], password=d['password'])

# @context_manager
# @context_manager('username', '123456')
# @context_manager2
# @context_manager2(d={'username': 'username', 'password': '123456'})
def execute(conn):
    pass

我想让四个装饰器中的任何一个成为可能,并且仍然能够以execute() 之类的方式调用execute

【问题讨论】:

  • 这不是关于如何修改代码,而是关于装饰器如何工作。 @decorator def function():...function = decorator(function) 相同,所以decorator 必须接受一个函数。此外,second 中的func 是什么?此名称未定义。
  • 不清楚您要做什么。你能给出一个示例输出吗?
  • @ForceBru 如有任何混淆,我们深表歉意。我正在随机尝试让事情正常运行...我忘了删除它。
  • @AdamSmith 对不起。我已经更新了我的帖子。

标签: python python-decorators


【解决方案1】:

看起来您可能只需要了解装饰器是什么。装饰器是一个函数,它接受一个函数作为其唯一参数,并在其位置返回一个函数。它们通常采用以下形式:

def decorator(f):
    def wrapped(*args, **kwargs):
        # it's important to accept any arguments to wrapped, thus *args and **kwargs
        # because then you can wrap _any_ function and simply pass its arguments on.
        print("Inside the wrapped function")

        retval = f(*args, **kwargs)  # pass the arguments through to the decorated function

        print("Exiting the wrapped function")

        return retval

    return wrapped

这让您可以执行以下操作:

@decorator
def my_increment(x):
    print("Calculating...")
    return x + 1

# actually equivalent to
def my_increment(x):
    print("Calculating...")
    return x + 1

my_increment = decorator(my_increment)

并期待如下结果:

>>> print(my_increment(3))
Inside the wrapped function
Calculating...
Exiting the wrapped function
4

值得注意的是:my_increment 在运行时成为修饰函数,在调用时不是。如果没有装饰器功能,您将无法调用 my_increment


您尝试执行的操作与您使用装饰器的操作完全不同。这对我来说就像函数链接。

def first(x=1):
    return [x]

def second(d=None):
    if d is None:
        d = {'x':10}  # why do this? https://stackoverflow.com/q/1132941/3058609
    return first(d['x'])

def third(lst):
    return lst + [-1]

然后这样称呼它:

# With @first,
# I am expecting to get [1, -1].
third(first())  # [1, -1]

# With @first(x=10),
# I am expecting to get [10, -1].
third(first(10))  # [10, -1]

# With @second,
# I am expecting to get [10, -1].
third(second())  # [10, -1]

# With @second({x:100}),
# I am expecting to get [100, -1].
third(second({'x':100}))  # [100, -1]

还要注意,装饰器可以接受参数,但是你说的是(请耐心等待......)一个接受参数的函数返回一个接受一个函数并返回一个函数的函数.你只是抽象了一层。想象一下:

def decorator_abstracted(msg):
    """Takes a message and returns a decorator"""

    # the below is almost the exact same code as my first example
    def decorator(f):
        def wrapped(*args, **kwargs):
            print(msg)
            retval = f(*args, **kwargs)
            print("exiting " + msg)
            return retval

        return wrapped
    return decorator

现在你的代码可能是

@decorator_abstracted("a decorator around my_increment")
def my_increment(x):
    print('Calculating...')
    return x + 1

【讨论】:

  • 它看起来不像装饰器的原因是我在抽象我的问题。我真正的问题是我想要一个为我处理打开和关闭连接的装饰器,这样我只需要编写代码来处理连接。并且连接需要参数,即first。我希望以不同的方式传递参数,即secondthird 是我要做的连接。我希望它像普通函数一样被调用,它还可以处理与装饰器的打开和关闭连接。抱歉,如果装饰器不应该以这种方式使用。
  • @SeakyLuo 听起来像是一个上下文管理器,虽然它可能是在装饰器的包装方法中使用的上下文管理器,也许?
  • 是的,我正在尝试在装饰器中实现上下文管理器。我不确定我的案子是否需要装饰师,但我真的很想练习。
  • @SeakyLuo 我添加了一个带有参数的装饰器函数示例。它们被进一步抽象了一步(你可以任意深入,但是,你知道... 不要 ),否则不会更复杂。
  • fdecorator_abstracted 中定义在哪里?我在我的first 中使用了func,因为我看到一些答案正在这样做,但我没有老实说……我以前把它放在first 的参数中,但这似乎使使用参数变得不可能。
猜你喜欢
  • 2021-08-24
  • 1970-01-01
  • 2018-11-22
  • 1970-01-01
  • 2012-03-02
  • 2021-12-28
  • 2019-05-09
  • 2019-05-30
  • 1970-01-01
相关资源
最近更新 更多