从装饰器访问 self

发布于 2024-12-07 06:06:25 字数 603 浏览 0 评论 0原文

在unittest的setUp()方法中,我设置了一些self变量,这些变量稍后在实际测试中引用。我还创建了一个装饰器来进行一些日志记录。有没有办法可以从装饰器访问这些 self 变量?

为了简单起见,我发布了这段代码:

def decorator(func):
    def _decorator(*args, **kwargs):
        # access a from TestSample
        func(*args, **kwargs)
    return _decorator

class TestSample(unittest.TestCase):    
    def setUp(self):
        self.a = 10

    def tearDown(self):
        # tear down code

    @decorator
    def test_a(self):
        # testing code goes here

What would be the best way of accessing a (setUp()) fromdecorator?

In setUp() method of unittest I've setup some self variables, which are later referenced in actual tests. I've also created a decorator to do some logging. Is there a way in which I can access those self variables from decorator?

For the sake of simplicity, I'm posting this code:

def decorator(func):
    def _decorator(*args, **kwargs):
        # access a from TestSample
        func(*args, **kwargs)
    return _decorator

class TestSample(unittest.TestCase):    
    def setUp(self):
        self.a = 10

    def tearDown(self):
        # tear down code

    @decorator
    def test_a(self):
        # testing code goes here

What would be the best way of accessing a (set in setUp()) from decorator?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

挖个坑埋了你 2024-12-14 06:06:26

由于您正在装饰一个方法,并且 self 是一个方法参数,因此您的装饰器可以在运行时访问 self 。显然不是在解析时,因为还没有对象,只是一个类。

所以你将你的装饰器更改为:

def decorator(func):
    def _decorator(self, *args, **kwargs):
        # access a from TestSample
        print 'self is %s' % self
        return func(self, *args, **kwargs)
    return _decorator

Since you're decorating a method, and self is a method argument, your decorator has access to self at runtime. Obviously not at parsetime, because there are no objects yet, just a class.

So you change your decorator to:

def decorator(func):
    def _decorator(self, *args, **kwargs):
        # access a from TestSample
        print 'self is %s' % self
        return func(self, *args, **kwargs)
    return _decorator
她比我温柔 2024-12-14 06:06:26

您实际上也可以使用 functools.wraps< /a> 保存help(obj.method)信息。除此之外,如果装饰器仅在类中使用,则可以将其包含在类主体中:

from functools import wraps


class MyClass:

    def decorator(func):
        @wraps(func)
        def _decorator(self, *args, **kwargs):
            print("In decorator. self: ", self)
            return func(self, *args, **kwargs)

        return _decorator

    @decorator
    def somemethod(self, a: int, b: int = 5):
        """This is somemethod.

        Parameters
        ----------
        a:
            This is a.
        b:
            The b is optional argument. (Default: 5)
        """
        print("in somemethod")

其工作原理如下

>>> obj = MyClass()
>>> obj.somemethod()
In decorator. self:  <__main__.MyClass object at 0x7f0378df6920>
in somemethod

,并且在调用 help() 时将打印原始文档字符串:

>>> help(obj.somemethod)

Help on method somemethod in module __main__:

somemethod(a: int, b: int = 5) method of __main__.MyClass instance
    This is somemethod.
    
    Parameters
    ----------
    a:
        This is a.
    b:
        The b is optional argument. (Default: 5)
:

而不是这样(当省略 @ 时)包裹(func)):

>>> help(obj.somemethod)

Help on method _decorator in module __main__:

_decorator(*args, **kwargs) method of __main__.MyClass instance
(END)

You could actually also use functools.wraps to save the help(obj.method) information. In addition to that, if the decorator is only used in the class, it could be included to the class body:

from functools import wraps


class MyClass:

    def decorator(func):
        @wraps(func)
        def _decorator(self, *args, **kwargs):
            print("In decorator. self: ", self)
            return func(self, *args, **kwargs)

        return _decorator

    @decorator
    def somemethod(self, a: int, b: int = 5):
        """This is somemethod.

        Parameters
        ----------
        a:
            This is a.
        b:
            The b is optional argument. (Default: 5)
        """
        print("in somemethod")

Which works like this

>>> obj = MyClass()
>>> obj.somemethod()
In decorator. self:  <__main__.MyClass object at 0x7f0378df6920>
in somemethod

and will print the original docstring when calling help():

>>> help(obj.somemethod)

Help on method somemethod in module __main__:

somemethod(a: int, b: int = 5) method of __main__.MyClass instance
    This is somemethod.
    
    Parameters
    ----------
    a:
        This is a.
    b:
        The b is optional argument. (Default: 5)
:

instead of this (when omitting @wraps(func)):

>>> help(obj.somemethod)

Help on method _decorator in module __main__:

_decorator(*args, **kwargs) method of __main__.MyClass instance
(END)
人生戏 2024-12-14 06:06:26

如果您使用基于类的装饰器实现,这里有一个示例。

import functools
from typing import Any, Callable


class UpdateBackupStatus:

    def __call__(self, f: Callable) -> Any:
        @functools.wraps(wrapped=f)
        def wrapper(calling_instance, *args, **kwargs):
            batch_id = calling_instance.batch_id
            if batch_id is None:
                raise ValueError(
                    "batch_id is required"
                )
            # pre-logic goes here
            response = f(calling_instance, *args, **kwargs)
            # post-logic goes here
            return response


我个人不喜欢在这种情况下使用 self 作为变量名来引用原始调用者。它可能会产生可读性问题。相反,使用不同的唯一名称来调用它(在本例中,我使用 - calling_instance

用法如下:

class SomeOtherClass:
    batch_id: str

    def __init__(
        self,
        batch_id: str,
    ):
        self.batch_id = batch_id

    @UpdateBackupStatus()
    def backup_table(self, table_name: str):
        # do something
        pass

SomeOtherClassbatch_id 属性是现在可以从装饰器访问。

If you are using class based decorator implementation., here is an example.

import functools
from typing import Any, Callable


class UpdateBackupStatus:

    def __call__(self, f: Callable) -> Any:
        @functools.wraps(wrapped=f)
        def wrapper(calling_instance, *args, **kwargs):
            batch_id = calling_instance.batch_id
            if batch_id is None:
                raise ValueError(
                    "batch_id is required"
                )
            # pre-logic goes here
            response = f(calling_instance, *args, **kwargs)
            # post-logic goes here
            return response


I personally don't like using self as the variable name in this context for referring the original caller. It might create readability issue. Instead call it with a different unique name ( in this example, I used - calling_instance )

Usage would be like :

class SomeOtherClass:
    batch_id: str

    def __init__(
        self,
        batch_id: str,
    ):
        self.batch_id = batch_id

    @UpdateBackupStatus()
    def backup_table(self, table_name: str):
        # do something
        pass

The batch_id property of SomeOtherClass is now accessible from decorator.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文