将额外的功能应用于直接实例方法

huangapple go评论66阅读模式
英文:

Apply an additional function to direct instance methods

问题

以下是您要翻译的代码段:

假设我有一些类

class MyBaseClass:
    def method1(self):
        print('Hello 1')

class MyClass2:
    def __init__(self, inp):
        self.old_class = inp

    def __getattr__(self, item):
        if hasattr(self, item):
            print('Hello!')
            return getattr(self, item)
        else:
            return getattr(self.old_class, item)  

    def method2(self):
        print('Hello 2')

我想要做的是,在直接作为该类的一部分定义的方法被调用时打印“Hello”。例如:

MyBaseClass().method1()
'Hello 1'
MyClass2(MyBaseClass()).method1()
'Hello 1'
MyClass2(MyBaseClass()).method2()
'Hello!'
'Hello 2'

我也意识到我的问题是__getattr__只有在__getattribute__失败时才会被调用。我的问题要么是:

  1. 如何定义__getattribute__以实现这一点,而不会遇到递归错误。

或者 2) 有没有其他东西可以替代__getattribute__来实现这一点。

我不想在MyClass2中定义的方法中添加print('Hello!')(例如,我想要一种可以推广到多个方法而不是在每个方法中都加入print('Hello!')的方法)。

英文:

Suppose I have a couple of classes:

class MyBaseClass:
    def method1(self):
        print('Hello 1')

class MyClass2:
    def __init__(self, inp):
        self.old_class = inp

    def __getattr__(self, item):
        if hasattr(self, item):
            print('Hello!')
            return getattr(self,item)
        else:
            return getattr(self.old_class, item)  

    def method2(self):
        print('Hello 2')

What I want to do is print "Hello" when the method called is defined directly as part of that class. For example:

MyBaseClass().method1()
'Hello 1'
MyClass2(MyBaseClass()).method1()
'Hello 1'
MyClass2(MyBaseClass()).method2()
'Hello!'
'Hello 2'

I am also aware that my issue is that __getattr__ isn't called unless __getattribute__ fails. My question is either:

  1. How can I define __getattribute__ to achieve this without running into recursion errors.

or 2) Is there something I can use instead of __getattribute__ to achieve this.

I don't want to add print('Hello!') to the method defined in MyClass2 (e.g. I would like something that generalises to multiple methods without putting print('Hello!') inside each of them.

答案1

得分: 1

Sounds like your code design/architecture is not so smart as it may be.

通常情况下,继承mixins通常用于扩展类:

class Base:
  def base_method(self):
    print("base")

class Sub(Base)
  pass

# 将打印 `base`
Sub().base_method()

如果你想动态地为新类分配新方法,可以使用type

class A:
  pass

def extra_method(self):
  print(self)

B = type("B", (A,), {"extra_method": extra_method})

# 将打印 `<__main__.B object at 0x7f0924521630>`
B().extra_method()

但假设你完全理解你想要实现的内容,代码设计的方式在你的问题中是一种可能的正确方式。

在这种情况下,你需要实现__getattribute__,如下所示:

class MyBaseClass:
    def method1(self):
        print('Hello 1')

class MyClass2:
    def __init__(self, inp):
        self.old_class = inp

    # 只有在要调用未在类内定义的属性时才会使用`__getattr__`(在你的情况下是`method1`)
    # 但是你希望在调用现有属性(`method2`)时打印`Hello!`,因此应该使用`__getattribute__`
    # `__getattribute__`将在任何情况下都会被调用
    def __getattribute__(self, item):
        # 这里为什么要调用`super()`?
        # 如果我们在当前类中调用`__getattribute__`,那么它会调用`__getattribute__`,然后再次调用`__getattribute__`
        # ...再次无限递归
        # 因此,我们可以使用父类的默认`__getattribute__`方法(父类是`object`)来避免递归
        my_attr = super().__getattribute__(item)
        
        # 为什么需要这个`if`?
        # 当我们在异常情况下调用`self.old_class()`时
        # 会再次在底层调用`__getattribute__`,因为`old_class`是`self`的属性
        # 我们不希望每次调用`old_class`时都打印`Hello!`,对吗?
        # 这就是这个`if`的目的
        if item != 'old_class':
            print('Hello!')
        # 最后,如果存在所需的属性,就返回它
        return my_attr

    def method2(self):
        print('Hello 2')
英文:

Sounds like your code design/architecture is not so smart as it may be.

Usually inheritance and mixins are using to extend classes:

class Base:
  def base_method(self):
    print(&quot;base&quot;)

class Sub(Base)
  pass

# will print `base`
Sub().base_method()

If you want to assign new methods to new classes dynamically you may use type:

class A:
  pass

def extra_method(self):
  print(self)

B = type(&quot;B&quot;, (A,), {&quot;extra_method&quot;: extra_method})

# will print &lt;__main__.B object at 0x7f0924521630&gt;
B().extra_method()

But let's suppose that you are completelly understanding what you want to implement and design of code in your question is one possible correct way.

In this case you have to implement __getattribute__ like:

class MyBaseClass:
    def method1(self):
        print(&#39;Hello 1&#39;)

class MyClass2:
    def __init__(self, inp):
        self.old_class = inp

    # `__getattr__` is using only when you want to call attribute that is not defined inside the class (`method1` in your case)
    # however you want to print `Hello!` on calling existant attribute (`method2`) so `__getattribute__` should be used
    # `__getattribute__` will be called in any case 
    def __getattribute__(self, item):
        # hasattr can&#39;t be used here
        # because hasattr is using `__getattribute__` under the hood
        # and `__getattribute__` is calling `hasattr` that is using `__getattribute__`
        # ...endless recursion
        # to solve this problem we may try to assign method to variable without checking is it exists or not
        # if it not exists internal method will be assigned
        # if not - AttributeError will be raised and method of another class will be called
        try:
            # why we call `super()` here?
            # if we call `__getattribute__` of current class that is calling `__getattribute__` that is calling `__getattribute__`
            # ... endless recursion again
            # So we may use default `__getattribute__` method of parent class (and parent class is `object`) to be able avoid recursion
            my_attr = super().__getattribute__(item)
        except AttributeError:
            # in other way default `__getattribute__` of `old_class`s instance will be called
            # that is simple
            return self.old_class().__getattribute__(item)

        # why we need this `if`?
        # when we calling `self.old_class()` on exception
        # `__getattribute__` is calling under the hood again because `old_class` is an attribute of `self`
        # we don&#39;t need to print `Hello!` every time when `old_class` is called, right?
        # and that is purpose of this `if`
        if item != &#39;old_class&#39;:
            print(&#39;Hello!&#39;)
        # finally return desired attribute if it exists
        return my_attr

    def method2(self):
        print(&#39;Hello 2&#39;)

huangapple
  • 本文由 发表于 2023年5月22日 18:38:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76305332.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定