简体   繁体   English

Python:如何通过作为参数传递的类对象调用类方法

[英]Python: how to call class methods through a class object passed as a parameter

I would like to pass a class as an argument to another class and then call methods of the class that I passed but I am getting different errors. 我想将一个类作为参数传递给另一个类,然后调用我传递的类的方法,但遇到了不同的错误。 I started with this but my code keeps giving me errors. 从此开始,但是我的代码不断给我错误。

Consider the following situation: 请考虑以下情况:

class A:
    A_class_var = "I am an A class variable"

    def __init__(self):
        self.A_inst_var = "I am an A instance variable"

    def GetClassVar(self):
        return A_class_var

    def GetInstVar(self):
        return self.A_inst_var

class B:

    def __init__(self, cls):
        print cls.GetClassVar()
        print cls.GetInstVar()

1) When I call b = B(A) I get an "unbound method GetClassVar() must be called with A instance as first argument (got nothing instead)." 1)当我调用b = B(A)我得到一个“必须以A实例作为第一个参数来调用未绑定方法GetClassVar()(而是什么也不做)。” So I figure that GetClassVar() and GetInstVar() expected an A instance and tried adding cls to the arguments of the two calls in B's __init__ . 因此,我认为GetClassVar()和GetInstVar()期望一个A实例,并尝试在B的__init__中将cls添加到两个调用的参数中。 Same error. 同样的错误。

2) When I tried calling b = B(A()) I get the error "global name A_class_var" is not defined. 2)当我尝试调用b = B(A()) ,出现错误“全局名称A_class_var”未定义。

3) When I tried calling a = A() followed by b = B(a) I got the same error as in #2. 3)当我尝试调用a = A()然后调用b = B(a) ,遇到了与#2中相同的错误。

4) When I tried to do the suggestion in the SO answer I linked above, namely change the definition of B to: 4)当我尝试在上面链接的SO答案中提出建议时,即将B的定义更改为:

class B:
    def __init__(self, cls):
        self.my_friend = cls

    def GetFriendsVariables(self):
        print my_friend.GetClassVar()
        print my_friend.GetInstVar()

and then call b = B(A) followed by b.GetFriendsVariables() (also following that SO I linked) I get the error "global name my_friend is not defined" in GetFriendsVariables . 然后调用b = B(A)接着b.GetFriendsVariables()以下也即SO我联的)我得到的错误中的“全局名称my_friend没有定义” GetFriendsVariables

I'd really like to understand how to properly pass classes as arguments so that I can access variables and methods of those classes and would love to understand why my various attempts above don't work (and yet the SO link I sent does?) 我真的很想了解如何正确地将类作为参数传递,以便我可以访问那些类的变量和方法,并且很想了解为什么我上面的各种尝试都行不通(而我发送的SO链接却可以呢?)

Thanks very much! 非常感谢!

Okay. 好的。 There's many points to address here! 这里有很多要点! :) :)

  • 1) You're right, GetClassVar and GetInstVar do expect an A instance. 1)你是对的, GetClassVarGetInstVar确实期望一个A实例。 You'll never make it work by calling with the class, unless you do: 除非您这样做,否则永远不会通过调用该类来使其正常工作:

     class A: @classmethod def GetClassVar(cls): return cls.A_class_var # use `cls.` to get the variable 

    If you're not familiar, the classmethod decorator will allow you pass the class as the first argument automatically (instead of the instance). 如果您不熟悉, classmethod装饰器将允许您自动将类作为第一个参数传递(而不是实例)。 So, calling the methods with the class name prepended (like cls.GetClassVar() ) is now guaranteed to work as you expected. 因此,现在可以保证调用带有类名前缀的方法(例如cls.GetClassVar() )可以按预期工作。


  • 2) You're getting that error exactly for the reason the interpreter says. 2)您得到该错误的原因恰恰是解释程序所说的原因。 There is no A_class_var declared in the global namespace. 全局名称空间中没有声明A_class_var Remember, it's inside of class A . 请记住,它在A类内部。 There are two solutions here (one is more implicit in nature, and the other more explicit ). 这里有两种解决方案(一种在本质上更隐式 ,另一种在显式上 )。

    Implicit: 隐式:

     def GetClassVar(self): return self.A_class_var 

    This works because when you specify an instance attribute, the instance searches its own __dict__ and if it can't find the attribute, it then searches the class' __dict__ . 之所以__dict__ ,是因为当您指定实例属性时,该实例将搜索其自己的__dict__ ,如果找不到该属性,则它将搜索该类的__dict__

    Explicit: 明确:

     def GetClassVar(self): return self.__class__.A_class_var 

    As you can see, it's possible to fetch the class object directly from the instance. 如您所见,可以直接从实例中获取类对象。 This searches the attribute directly inside of the class as opposed to checking if it exists inside the instance first. 这将直接在类内部搜索属性,而不是首先检查实例中是否存在该属性。 This has the added benefit of being clearer and avoiding issues where an instance and class attribute name may be the same. 这具有更清晰的优点,并且避免了实例和类属性名称可能相同的问题。

    Even more explicit : (Expanding on user 6502's answer) 更明确的是 :(扩展用户6502的答案)

     def GetClassVar(self): return A.A_class_var 

    Personally, I don't really like doing it this way. 就个人而言,我真的不喜欢这样。 Having to repeat the name of the class feels wrong to me , but depending on your circumstances this may not be such a bad thing. 对我来说 ,不得不重复上课的名称感觉很不对,但是根据您的情况,这可能不是一件坏事。


  • 3) You're getting the error for the same reasons I've just outlined. 3)由于我刚才概述的相同原因,您遇到了错误。

  • 4) This won't work any better, again, for the same reasons as above. 4)同样,由于上述相同的原因,这种方法也不会更好。 However, you do have issues to resolve here as well: 但是,您也确实有一些问题需要解决:

     class B: def __init__(self, cls): self.my_friend = cls def GetFriendsVariables(self): print my_friend.GetClassVar() # should be: self.my_friend.GetClassVar() print my_friend.GetInstvar() # should be: self.my_friend.GetInstvar() 

    Notice you still have to prepend the my_friend variable with self. 注意,您仍然必须在my_friend变量前加上self. . But as I've told you, calling it this way will get you the same result. 但是,正如我已经告诉您的那样,以这种方式调用将获得相同的结果。 The problem is with how you've defined the method GetClassVar() . 问题在于您如何定义方法GetClassVar() You'll see that if you use the @classmethod decorator (or pass an instance and change the attribute fetch to any of the two solutions I've given you above---the implicit or explicit forms), it'll start working. 您会看到,如果使用@classmethod装饰器(或传递实例并将属性fetch更改为我上面给您的两种解决方案中的任何一种(隐式或显式形式)),它将开始工作。 ;) ;)

A direct call may doesn't work, but you can use getattr and hasattr to make it work. 直接调用可能不起作用,但是您可以使用getattr和hasattr使其起作用。

class A:
    A_class_var = "I am an A class variable"

    def __init__(self):
        self.A_inst_var = "I am an A instance variable"

    @staticmethod  # If it's a classmethod, use the staticmethod to decorate the method
    def GetClassVar(cls):
        return cls.A_class_var

    def GetInstVar(self):
        return self.A_inst_var


class B:
    def __init__(self, cls):
        if hasattr(cls, "GetClassVar"):
            func = getattr(cls, "GetClassVar")
            func(cls)
        b = cls()
        b.GetInstVar()

if __name__ == '__main__':
    b  = B(A)

Python scope rules are different from C++. Python范围规则不同于C ++。 More specifically like you always have to use explicitly self.<membername> when accessing instance members you also have to explicitly use <classname>.<membername> when accessing class members: 更具体地说,就像访问实例成员时始终必须显式使用self.<membername> ,访问类成员时也必须显式使用<classname>.<membername>

class A:
    A_class_var = "I am an A class variable"

    def __init__(self):
        self.A_inst_var = "I am an A instance variable"

    def GetClassVar(self):
        return A.A_class_var

    def GetInstVar(self):
        return self.A_inst_var

class B:
    def __init__(self, cls):
        print cls.GetClassVar()
        print cls.GetInstVar()

With this small change A.A_class_var the code works as you expect 有了这个小改动A.A_class_var ,代码即可按预期工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM