简体   繁体   English

Python使用装饰器实现上下文管理器

[英]Python use decorator to implement context manager

I am trying to make two decorators with parameters. 我试图用参数制作两个装饰器。 First creates a list with element x and calls func . First使用元素x创建一个list ,然后调用func second is simply calling the first by passing a parameter from a dict. second是简单地通过传递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 function simply modifies the list passed in. I want to make any of the four decorators below possible by simply calling third() . third函数只是修改传入的列表。我想通过简单地调用third()使下面的四个装饰器中的任何一个成为可能。 How should I modify my code? 我应该如何修改我的代码?

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

third()

For example: 例如:

## 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].

The upper code is an abstraction of my problem. 上层代码是我的问题的抽象。 My real problem is that I want a decorator that handles opening and closing connection for me, so that I only need to write code for handling the connection. 我的真正问题是我想要一个装饰器来为我处理打开和关闭连接,因此我只需要编写代码来处理连接。

And the connection needs parameters, which is first . 连接需要参数,这是first I want the parameters to be passed in a different way, which is the second . 我希望以另一种方式传递参数,这是second third is what I am gonna do with the connection. third是我将如何处理连接。 I want third to be called like a normal function, and it also handles the opening and closing connection using a decorator. 我希望third函数像普通函数一样被调用,并且它还使用装饰器处理打开和关闭连接。 Sorry if decorator should not be used this way, but I really want to practice using it. 抱歉,如果不能以这种方式使用装饰器,但是我真的想练习使用它。

---Update--- ---更新---

What I want to achieve is basically the following: 我要实现的基本上是以下内容:

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

I want to make any of the four decorators possible and still be able to call execute in a way like execute() 我想使四个装饰器中的任何一个都可以,并且仍然能够像execute()一样调用execute

Looks like you maybe just need a primer on what a decorator is. 看起来您可能只需要了解装饰器是什么。 A decorator is a function that accepts a function as its only argument, and returns a function in its place. 装饰器是一个接受函数作为其唯一参数的函数,并在其位置返回一个函数。 They often take the form of: 它们通常采取以下形式:

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

This lets you do something like: 这使您可以执行以下操作:

@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)

and expect results like: 并预期结果如下:

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

Notably: my_increment becomes the decorated function at runtime, not at call time. 值得注意的是: my_increment在运行时而不是在调用时成为修饰的函数。 You can't call my_increment without the decorator functionality. 没有装饰器功能就无法调用my_increment


What you're attempting to do doesn't look anything like what you'd use a decorator for. 您尝试执行的操作看起来与使用装饰器的外观看起来不一样。 This looks like function chaining to me. 这对我来说就像函数链。

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]

and call it like: 并这样称呼:

# 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]

Note also that decorators can take parameters, but then you're talking about (bear with me...) a function that takes parameters that returns a function which takes a function and returns a function. 还要注意,装饰器可以接受参数,但是您正在谈论的是(带我...)一个接受参数的函数,该参数返回一个函数,该函数接受一个函数并返回一个函数。 You're just abstracting one more layer. 您只是抽象了一层。 Imagine: 想像:

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

Now your code could be 现在您的代码可能是

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM