Python 包装类方法

发布于 2024-11-25 09:30:58 字数 720 浏览 2 评论 0原文

我正在尝试创建一个带有 run 方法的对象,该方法将由 _wrap_run 方法 包装。我希望能够通过简单地键入 instance.run() 来调用该方法及其包装器,并且我希望能够对该对象进行子类化,以便我可以覆盖 run () 方法并让它仍然执行包装器。

更简单地说,我希望人们能够子类化 A 并重写 run(),但仍然可以调用 run() 方法来执行包装器函数。

我在这个机制上遇到了一些困难。有人对这种方法有什么建议吗?

class A:

    def run(self):
        print "Run A"
        return True

    def _wrap_run(self):
        print "PRE"
        return_value = self.run()
        print "POST"
        return return_value

    run = property(_wrap_run)


a = A()
a.run()
"""
Should Print: 
PRE
Run A
POST
"""


class B(A):

    def run(self):
        print "Run B"
        return True

b = B()
b.run()
"""
Should Print: 
PRE
Run B
POST
"""

I'm trying to create an object with a run method that will be wrapped by a _wrap_run method. I'd like to be able to call the method and it's wrapper by simply typing instance.run() and I'd like to be able to subclass the object so I can override the run() method and have it still execute the wrapper.

More simply put, I want people to be able to subclass A and override run() but still have calls to the run() method execute the wrapper function.

I'm having some difficulty with the mechanics of this. Does anyone have any suggestions regarding this approach?

class A:

    def run(self):
        print "Run A"
        return True

    def _wrap_run(self):
        print "PRE"
        return_value = self.run()
        print "POST"
        return return_value

    run = property(_wrap_run)


a = A()
a.run()
"""
Should Print: 
PRE
Run A
POST
"""


class B(A):

    def run(self):
        print "Run B"
        return True

b = B()
b.run()
"""
Should Print: 
PRE
Run B
POST
"""

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

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

发布评论

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

评论(5

分开我的手 2024-12-02 09:30:58

使用元类。

class MetaClass(type):
    @staticmethod
    def wrap(run):
        """Return a wrapped instance method"""
        def outer(self):
            print "PRE",
            return_value = run(self)
            print "POST"
            return return_value
        return outer
    def __new__(cls, name, bases, attrs):
        """If the class has a 'run' method, wrap it"""
        if 'run' in attrs:
            attrs['run'] = cls.wrap(attrs['run'])
        return super(MetaClass, cls).__new__(cls, name, bases, attrs)

class MyClass(object):
    """Use MetaClass to make this class"""
    __metaclass__ = MetaClass
    def run(self): print 'RUN',

myinstance = MyClass()

# Prints PRE RUN POST
myinstance.run()

现在,如果其他人子类化 MyClass,他们仍然会包装他们的 run() 方法。

Use a Metaclass.

class MetaClass(type):
    @staticmethod
    def wrap(run):
        """Return a wrapped instance method"""
        def outer(self):
            print "PRE",
            return_value = run(self)
            print "POST"
            return return_value
        return outer
    def __new__(cls, name, bases, attrs):
        """If the class has a 'run' method, wrap it"""
        if 'run' in attrs:
            attrs['run'] = cls.wrap(attrs['run'])
        return super(MetaClass, cls).__new__(cls, name, bases, attrs)

class MyClass(object):
    """Use MetaClass to make this class"""
    __metaclass__ = MetaClass
    def run(self): print 'RUN',

myinstance = MyClass()

# Prints PRE RUN POST
myinstance.run()

Now if other people subclass MyClass, they will still get their run() methods wrapped.

初见你 2024-12-02 09:30:58

最简单的方法:让 run 包装器,并将私有方法作为可重写的方法。

class A(object):
    def run(self):
        print "PRE"
        return_value = self._inner_run()
        print "POST"
        return return_value

    def _inner_run(self):
        print "Run A"
        return True

class B(A):
    def _inner_run(self):
        print "Run B"
        return True

Easiest way: make run the wrapper, and a private method be the overrideable one.

class A(object):
    def run(self):
        print "PRE"
        return_value = self._inner_run()
        print "POST"
        return return_value

    def _inner_run(self):
        print "Run A"
        return True

class B(A):
    def _inner_run(self):
        print "Run B"
        return True
谈情不如逗狗 2024-12-02 09:30:58

其他人所做的

class A:
   def do_run( self ):
       """Must be overridden."""
       raise NotImplementedError
   def run( self, *args, **kw ):
       """Must not be overridden.
       You were warned.
       """
       print "PRE"
       return_value = self.do_run(*args, **kw)
       print "POST"
       return return_value

class B(A):
    def do_run(self):
        print "Run B"
        return True

通常就足够了。

如果您想担心有人“破坏”这一点,请立即停止。不要浪费时间担心。

这是Python。我们这里都是成年人了。所有恶意的反社会者都会通过复制、更改然后破坏代码来破坏您的所有代码。无论你做什么,他们都会复制你的代码并修改它以破坏聪明的部分。

其他人都会阅读您的评论并遵守您的规则。如果他们想使用你的模块/包/框架,他们会合作。

What other folks do

class A:
   def do_run( self ):
       """Must be overridden."""
       raise NotImplementedError
   def run( self, *args, **kw ):
       """Must not be overridden.
       You were warned.
       """
       print "PRE"
       return_value = self.do_run(*args, **kw)
       print "POST"
       return return_value

class B(A):
    def do_run(self):
        print "Run B"
        return True

That's usually sufficient.

If you want to worry about someone "breaking" this, stop now. Don't waste time worrying.

It's Python. We're all adults here. All the malicious sociopaths will break all you code by copying it, changing it, and then breaking it. No matter what you do, they'll just copy your code and modify it to break the clever bits.

Everyone else will read your comment and stick by your rules. If they want to use your module/package/framework, they will cooperate.

因为看清所以看轻 2024-12-02 09:30:58

稍后,但如果该方法是普通方法,您可以使用常规装饰器。以下面的示例为例,它是一个简化的 SSH 连接器。

class SSH:

    def __init__(self) -> None:
        self.connected = False

    def connect(self) -> None:
        self.connected = True

    def call_only_if_connected(self) -> None:
        print("foo")

在此示例中,我们只希望在实例化的 SSH 对象具有 self.connected=True 时调用方法 call_only_if_connected(顾名思义)。

我们可以在类外部定义一个普通的包装器,并且由于self或类的实例始终作为第一个参数传递(args[0]),我们可以在那里进行检查。考虑:

def assert_connected(f):
    @wraps(f)
    def decorator(*args, **kwargs):
        if not args[0].connected:
            raise RuntimeError("attempted to call a method that requires a connection without an established connection")
        return f(*args, **kwargs)
    return decorator

class SSH:

    def __init__(self) -> None:
        self.connected = False

    def connect(self) -> None:
        self.connected = True

    @assert_connected
    def call_only_if_connected(self) -> None:
        print("foo")

请注意,在上面的示例中,如果对象没有 connected 属性,将会抛出 AttributeError ,因此定义 connected=False< /code> 尽快(即,一旦在 __init__ 中实例化该对象)。

您可以按照自己的意愿处理不正确的连接;在上面的示例中,我抛出了一个RuntimeError

Here a while later, but if the method is a normal method, you could just use a regular decorator. Take the example below, which is a simplified SSH connector.

class SSH:

    def __init__(self) -> None:
        self.connected = False

    def connect(self) -> None:
        self.connected = True

    def call_only_if_connected(self) -> None:
        print("foo")

In this example, we only want the method call_only_if_connected to be called, as the name implies, if the SSH object that it is instantiated to has self.connected=True.

We can define a normal wrapper outside the class, and since self, or the instance of the class, is always passed as the first arg (args[0]), we can just make out check there. Consider:

def assert_connected(f):
    @wraps(f)
    def decorator(*args, **kwargs):
        if not args[0].connected:
            raise RuntimeError("attempted to call a method that requires a connection without an established connection")
        return f(*args, **kwargs)
    return decorator

class SSH:

    def __init__(self) -> None:
        self.connected = False

    def connect(self) -> None:
        self.connected = True

    @assert_connected
    def call_only_if_connected(self) -> None:
        print("foo")

Note in the above example that an AttributeError will be thrown if the object does not have a connected property, so it is important to define connected=False as soon as possible (i.e., as soon as you instantiate the object in __init__).

You could handle the improper connection how you'd like; in my example above, I am throwing a RuntimeError.

坠似风落 2024-12-02 09:30:58

你所拥有的基本上是一个装饰器,所以为什么不继续实现_wrap_run 作为装饰器并在子类化函数时应用它?

What you have there is basically a decorator, so why not go ahead and implement _wrap_run as a decorator and apply it when subclassing the function?

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