用参数装饰类方法

时间:2019-07-06 23:25:31

标签: python

如何用参数修饰类方法?当前代码是

def establish_con(func):

    con, meta = db.connect(config.user, config.password, config.db)
    meta.reflect(bind=con)

    def inner(self, query, con, *args, **kwargs):
        return func(self, query, con, *args, **kwargs)

    con.close()
    return inner

class DataReader:
    def __init__(self):
        self.data = {}

    @establish_con
    def execQuery(self, query, con):
        # con, meta = db.connect(config.user, config.password, config.db)
        # meta.reflect(bind=con)

        result = pd.read_sql(query, con)

        # con.close()
        return result

test = DataReader()
df = test.execQuery("Select * from backtest limit 10")
print(df)

当前,第一个参数似乎是类实例。我尝试了不同的代码变体,但总是遇到太多/不足/未定义参数的问题。

我读过其他帖子

How do I pass extra arguments to a Python decorator?

Decorators with parameters?

和其他人,但仍然无法弄清楚。

编辑:不是Python decorators in classes的重复项,因为在此答案中,无需向函数传递任何参数。

3 个答案:

答案 0 :(得分:1)

@madjardi从this post改编答案以使用参数。

import functools

class Example:

    con = "Connection"

    def wrapper(func):
        @functools.wraps(func)
        def wrap(self, *args, **kwargs):
            print("inside wrap")
            return func(self, Example.con, *args, **kwargs) # use *args to pass objects down
        return wrap

    @wrapper
    def method(self, con, arg): 
        print("METHOD {0} {1}".format(con, arg))

    wrapper = staticmethod(wrapper)


e = Example()
e.method(1) # METHOD Connection 1

答案 1 :(得分:1)

您应该替换:

def inner(self, query, con, *args, **kwargs):
        return func(self, query, con, *args, **kwargs)

使用:

def inner(self, query, *args, **kwargs):   # no 'con' here
        return func(self, query, con, *args, **kwargs) 

使用您的策略的一个最小的工作示例将是(实现目标所没有的目标)

def add_arg(method):

    def decorated_method(self, *args):
        return method(self, 10, *args)

    return decorated_method

class Data:
    @add_arg
    def summation(self, *args):
        return sum(args)


d = Data()
print(d.summation(1, 2))  # prints 13, not 3

答案 2 :(得分:1)

这就是我认为您需要这样做的方式。为了将代码更清楚,我将代码分成两个单独的块。

Just的第一部分建立了一个最小的脚手架,以使其可以运行(并跟随执行)第二个块中的代码,并使它尽可能地接近问题所在。它本身并不十分重要。

带有参数的装饰器有效地成为了装饰器工厂,从某种意义上说,它们必须创建一个返回的装饰器函数,然后将其应用于目标函数或方法。

# Scaffolding
class Pandas:
    @staticmethod
    def read_sql(query, con):
        print(f'in Pandas read_sql({query!r}, {con})')
        return 'pandas_dataframe'


class Connection:
    def close(self):
        print('in Connection.close()')

    def __repr__(self):
        return '<Connection object>'

con = Connection()


class Meta:
    def reflect(self, bind=con):
        print(f'in Meta.reflect(bind={bind}')


class Database:
    def connect(self, user, password, db):
        print(f'in Database.connect({user}, {password}, {db})')
        return Connection(), Meta()

    def __repr__(self):
        return '<Database object>'

class Config:
    def __init__(self, user, password, db):
        self.user = user
        self.password = password
        self.db = db

# Set up a framework for testing.
pd = Pandas()
meta = Meta()
db = Database()
config = Config('username', 'secret', db)

在建立该环境之后,这就是装饰器的编写方式。

# Decorator
def establish_con(config):

    def wrapper(method):

        def wrapped(*args, **kwargs):
            con, meta = db.connect(config.user, config.password, config.db)
            meta.reflect(bind=con)

            args = args + (con,)
            result = method(*args, **kwargs)

            con.close()

            return result

        return wrapped

    return wrapper


class DataReader:
    def __init__(self):
        self.data = {}

    @establish_con(config)
    def execQuery(self, query, con):
        return pd.read_sql(query, con)


test = DataReader()
df = test.execQuery("Select * from backtest limit 10")
print(df)

输出:

in Database.connect(username, secret, <Database object>)
in Meta.reflect(bind=<Connection object>
in Pandas read_sql('Select * from backtest limit 10', <Connection object>)
in Connection.close()
pandas_dataframe