Ruby有一个send()方法,可以把任何参数(包括方法的调用)传入任何对象,至于能不能接收是被传入对象的事儿。
比如 subject.send(name, *args)

问题是,Python能不能实现类似功能呢?

我知道Python里有send(),但只不能传入类、方法和对象(貌似被传入对象还必须iterable)。。。

以下是我练习代理模式的案例:

class AccountProxy:
    def __init__(self,real_account):
        self.subject = real_account

    def proxy(self, method):
        print("Delegating method: ", method)
        self.subject.method   #这里出了问题

其中subject是创建的被代理的对象,这里是BankAccount,非常简单,有存钱取钱功能。
然后代理类AccountProxy 中的proxy方法是希望达到把method传入后由subject调用。
但是这里方法体内的method被当作名叫“method”的方法,解释器报错BankAccount没有method这个attribute。

有什么解决方法吗?

class C:
    def __init__(self, sth):
        self.sth = sth
    def say(self):
        print(f"Say {self.sth}")

c = C("c")
say_method = c.__getattribute__('say')

try:
    method_missing = c.__getattribute__('abc')
except:
    method_missing = None

c.say()
say_method()
if callable(method_missing):
    method_missing()

觉得你不能把两种语言的同名函数直接套用在另一个语言上。Ruby还能重载method_missing呢
对Python来说对象中的方法跟attr是一样的。关键在于有没有call
https://docs.python.org/3.7/library/functions.html?highlight=callable#callable

    0x709394 我在使用函数之前全都查了文档。之前说的send()正是在method_missing重载中使用,而Python无法做到重载method_missing,于是我退而求其次。

    在你的建议基础上我修改了一下代码,可惜仍然无法运行:

    ##省略
    
        def proxy(self, method):
            print("Delegating method: ", method)
            try:
                method_missing =self.subject.__getattribute__(method)
            except:
                method_missing = None
            if callable(method_missing):
                self.subject.method_missing()
    
    
    
    # driver
    ap= AccountProxy_Proxy(BankAccount("Mingming"))
    
    ap.proxy(deposit(33))
    ap.proxy(printBalance())

    报错是: NameError: name 'deposit' is not defined 并没有任何其他运行
    也就是说解释一开始就出错了。

      class AccountProxy:
          def __init__(self,real_account):
              self.subject = real_account
      
          def proxy(self, method_name, *argv):
              try:
                  method = self.subject.__getattribute__(method_name)
              except:
                  method = None
              if callable(method):
                  method(*argv)
              else:
                  raise Exception(f"{method_name} is not callable")
      
      class Account:
          def __init__(self, balance):
              self.balance = balance
          def printBalance(self):
              print(self.balance)
      
      p = AccountProxy(Account(996251404))
      p.proxy('printBalance')

        0x709394

        多谢!

        除了__getattribute__参数的问题,这里有两处我自己完全没想到的地方: 一是把方法名和参数分开,二是method = self.subject.__getattribute__(method_name)之后不需要从subject调用。这两个地方全是基本的OOP的概念,正如你所说重要答案就在之前我提问过的那个地方「可惜没能深入理解」!!

        Python不愧是程序员写给程序员的编程语言。

          NTL01 这两个地方全是基本的OOP的概念

          这里什么意思?我不懂了

            0x709394
            1 方法(类内函数)本身作为类的一个属性(attribute),而这个属性本身也是个对象,Python群体里不是非常流行“一切皆对象”吗?于是乎,方法的参数本身也是对象(真的是!),那方法和方法参数当然要分开对待成两个对象了。

            2 既然是对象,实体化之后(已经从self.subject里面getattribute了,此时referencing environment已经齐全)当然是直接调用/操作咯。

            每个小白都会试图建立自己的理解体系= =

            15 天 后

            学了一点Java的reflection之后我发现貌似这个东西在Java里也可以实现,之前以为只有动态语言才能实现。

            虽然我的试验(非常正常地)失败了,不过我相信问题出在个别细节上,因为还没有对reflection有非常系统的了解。

            我把代码发上了,感兴趣的朋友欢迎指出问题啊

            import java.lang.reflect.InvocationTargetException;
            import java.lang.reflect.Method;
            
            public class AccountProxy {
                public BankAccount realAccount;
            
                public AccountProxy(BankAccount realAccount) {
                    this.realAccount = realAccount;
                }
            
            
                public Object callMethod(String methodName, String ... params){
                    Class cls = realAccount.getClass();
                    try{
                        Method method=cls.getMethod(methodName);
                        Object returned= method.invoke(realAccount,params);
                        return returned;
                    }
                    catch (NoSuchMethodException e){
                        throw new IllegalArgumentException(cls.getName() + " does not support "+methodName);
                    }
                    catch (IllegalAccessException e){
                        throw new IllegalArgumentException("Insufficient access permission to call" +methodName + " in class "+ cls.getName());
                    }
                    catch (InvocationTargetException e){
                        throw new RuntimeException(e);
                    }
            
                }
            
            }
            public class AccountProxyRunner {
            
                public static void main(String[] args) {
                    AccountProxy firstAccount = new AccountProxy(new BankAccount("Shawn"));
            
                    firstAccount.callMethod("deposit","30");
                    System.out.println(firstAccount.callMethod("getBalance"));
                }
            }

            以及被代理的

            public class BankAccount {
                private String owner;//will be directed assigned value to ensure uniqueness of bank owner
                private double balance;
            
                public BankAccount(String name) {
                    owner = name;
                    balance=0;
                }
            
                public void deposit(double amount){
                    balance += amount;
                }
            
                public void withdraw(double amount){
                    balance -=amount;
                }
            
                public double getBalance() {
                    return balance;
                }
            
            }

            程序运行结果是抛出java.lang.IllegalArgumentException: BankAccount_Proxy.BankAccount does not support deposit ,发生了无方法异常。

            然而去掉firstAccount.callMethod("deposit","30");之后下面的调用getBalance()能正常运行。

            © 2018-2025 0xFFFF