Python:常规方法和名称相同的静态方法

时间:2022-02-01 16:54:47

Introduction

I have a Python class, which contains a number of methods. I want one of those methods to have a static counterpart—that is, a static method with the same name—which can handle more arguments. After some searching, I have found that I can use the @staticmethod decorator to create a static method.

我有一个Python类,它包含许多方法。我希望其中一个方法有一个静态的对位元——也就是一个名称相同的静态方法——它可以处理更多的参数。经过一些搜索,我发现可以使用@staticmethod decorator创建一个静态方法。

Problem

For convenience, I have created a reduced test case which reproduces the issue:

为了方便起见,我创建了一个简化的测试用例来重现问题:

class myclass:

    @staticmethod
    def foo():
        return 'static method'

    def foo(self):
        return 'public method'

obj = myclass()
print(obj.foo())
print(myclass.foo())

I expect that the code above will print the following:

我期望上述守则会刊载以下内容:

public method
static method

However, the code prints the following:

但是,代码打印如下:

public method
Traceback (most recent call last):
  File "sandbox.py", line 14, in <module>
    print(myclass.foo())
TypeError: foo() missing 1 required positional argument: 'self'

From this, I can only assume that calling myclass.foo() tries to call its non-static counterpart with no arguments (which won't work because non-static methods always accept the argument self). This behavior baffles me, because I expect any call to the static method to actually call the static method.

从这里,我只能假设调用myclass.foo()尝试在没有参数的情况下调用它的非静态对应物(这是行不通的,因为非静态方法总是接受自变量)。这种行为令我困惑,因为我希望对静态方法的任何调用都实际调用静态方法。

I've tested the issue in both Python 2.7 and 3.3, only to receive the same error.

我已经在Python 2.7和3.3中测试了这个问题,结果得到了相同的错误。

Questions

Why does this happen, and what can I do to fix my code so it prints:

为什么会发生这种情况,我该怎么做才能修复我的代码,让它打印出来:

public method
static method

as I would expect?

正如我所期望的?

5 个解决方案

#1


9  

A similar question is here: override methods with same name in python programming

这里有一个类似的问题:在python编程中使用同名的重写方法

functions are looked up by name, so you are just redefining foo with an instance method. There is no such thing as an overloaded function in Python. You either write a new function with a separate name, or you provide the arguments in such a way that it can handle the logic for both.

函数是按名称查找的,所以您只是用实例方法重新定义foo。在Python中不存在重载函数。您可以用一个单独的名称编写一个新函数,或者以这样的方式提供参数,以便它可以同时处理这两个函数的逻辑。

In other words, you can't have a static version and an instance version of the same name. If you look at its vars you'll see one foo.

换句话说,您不能有一个静态版本和一个同名的实例版本。如果你看它的vars,你会看到一个foo。

In [1]: class Test:
   ...:     @staticmethod
   ...:     def foo():
   ...:         print 'static'
   ...:     def foo(self):
   ...:         print 'instance'
   ...:         

In [2]: t = Test()

In [3]: t.foo()
instance

In [6]: vars(Test)
Out[6]: {'__doc__': None, '__module__': '__main__', 'foo': <function __main__.foo>}

#2


4  

Because attribute lookup in Python is something within the programmer's control, this sort of thing is technically possible. If you put any value into writing code in a "pythonic" way (using the preferred conventions and idioms of the python community), it is very likely the wrong way to frame a problem / design. But if you know how descriptors can allow you to control attribute lookup, and how functions become bound functions (hint: functions are descriptors), you can accomplish code that is roughly what you want.

因为Python中的属性查找是程序员可以控制的,所以从技术上讲,这类事情是可能的。如果您将任何值以“python”的方式编写代码(使用python社区的首选惯例和习惯用语),那么就很有可能是错误的方式来构造问题/设计。但是,如果您知道描述符如何允许您控制属性查找,以及函数如何成为绑定函数(提示:函数是描述符),那么您就可以完成大致是您想要的代码。

For a given name, there is only one object that will be looked up on a class, regardless of whether you are looking the name up on an instance of the class, or the class itself. Thus, the thing that you're looking up has to deal with the two cases, and dispatch appropriately.

对于给定的名称,无论您是在类的实例上查找名称,还是在类本身上查找名称,在类上只查找一个对象。因此,您正在查找的东西必须处理这两种情况,并适当地发送。

(Note: this isn't exactly true; if an instance has a name in its attribute namespace that collides with one in the namespace of its class, the value on the instance will win in some circumstances. But even in those circumstances, it won't become a "bound method" in the way that you probably would wish it to.)

(注:事实并非如此;如果实例的属性名称空间中有一个名称,该名称空间与其类的名称空间中的名称发生冲突,那么在某些情况下实例上的值将获胜。但即使在这种情况下,它也不会以你可能希望的方式成为一种“绑定方法”。

I don't recommend designing your program using a technique such as this, but the following will do roughly what you asked. Understanding how this works requires a relatively deep understanding of python as a language.

我不建议您使用这样的技术来设计您的程序,但是下面将大致完成您所要求的工作。要理解这是如何工作的,需要对python作为一种语言有一个相对深入的理解。

class StaticOrInstanceDescriptor(object):

    def __get__(self, cls, inst):
        if cls is None:
            return self.instance.__get__(self)
        else:
            return self.static

    def __init__(self, static):
        self.static = static

    def instance(self, instance):
        self.instance = instance
        return self


class MyClass(object):

    @StaticOrInstanceDescriptor
    def foo():
        return 'static method'

    @foo.instance
    def foo(self):
        return 'public method'

obj = MyClass()
print(obj.foo())
print(MyClass.foo())

which does print out:

并打印出:

% python /tmp/sandbox.py
static method
public method

#3


3  

While it's not strictly possible to do, as rightly pointed out, you could always "fake" it by redefining the method on instantiation, like this:

正如正确指出的那样,虽然严格来说不可能做到这一点,但您可以通过重新定义实例化方法来“伪造”它,如下所示:

class YourClass(object):

    def __init__(self):
        self.foo = self._instance_foo

    @staticmethod
    def foo()
        print "Static!"

    def _instance_foo(self):
        print "Instance!"

which would produce the desired result:

这将产生预期的结果:

>>> YourClass.foo()
Static!
>>> your_instance = YourClass()
>>> your_instance.foo()
Instance!

#4


1  

Ended up here from google so thought I would post my solution to this "problem"...

结束于谷歌,所以我想把我的解决方案发布到这个“问题”上……

class Test():
    def test_method(self=None):
        if self is None:
            print("static bit")
        else:
            print("instance bit")

This way you can use the method like a static method or like an instance method.

通过这种方法,您可以使用像静态方法或实例方法这样的方法。

#5


0  

I'm really ignorant in Python when it comes to these types of questions but I agree with a number of the sentiments in the answers so far that it's not native and you should not try.

当涉及到这些类型的问题时,我在Python中真的很无知,但是我同意到目前为止答案中的一些观点,因为它不是原生的,你不应该尝试。

So, now for something completely different. I just mocked up the following for a similar problem I have:

所以,现在来看看完全不同的东西。我刚刚模拟了一个类似的问题:

class Foo(object):
    def __init__(self, name):
        self.name = name

    @staticmethod
    def get_norm_name(name):
        # Normalize name
        return name.strip().lower()

    def norm_name(self):
        return Foo.get_norm_name(self.name)

>>> Foo.get_norm_name('Robert')
'robert'

>>> f = Foo('Robert')
>>> f.norm_name()
'robert'

The method names are different, which you were trying to avoid, but I think they're close enough. Again, not sure if this is Pythonic, but it seems easy to understand.

方法名是不同的,这是您试图避免的,但是我认为它们非常接近。同样,不确定这是不是python的,但是看起来很容易理解。

#1


9  

A similar question is here: override methods with same name in python programming

这里有一个类似的问题:在python编程中使用同名的重写方法

functions are looked up by name, so you are just redefining foo with an instance method. There is no such thing as an overloaded function in Python. You either write a new function with a separate name, or you provide the arguments in such a way that it can handle the logic for both.

函数是按名称查找的,所以您只是用实例方法重新定义foo。在Python中不存在重载函数。您可以用一个单独的名称编写一个新函数,或者以这样的方式提供参数,以便它可以同时处理这两个函数的逻辑。

In other words, you can't have a static version and an instance version of the same name. If you look at its vars you'll see one foo.

换句话说,您不能有一个静态版本和一个同名的实例版本。如果你看它的vars,你会看到一个foo。

In [1]: class Test:
   ...:     @staticmethod
   ...:     def foo():
   ...:         print 'static'
   ...:     def foo(self):
   ...:         print 'instance'
   ...:         

In [2]: t = Test()

In [3]: t.foo()
instance

In [6]: vars(Test)
Out[6]: {'__doc__': None, '__module__': '__main__', 'foo': <function __main__.foo>}

#2


4  

Because attribute lookup in Python is something within the programmer's control, this sort of thing is technically possible. If you put any value into writing code in a "pythonic" way (using the preferred conventions and idioms of the python community), it is very likely the wrong way to frame a problem / design. But if you know how descriptors can allow you to control attribute lookup, and how functions become bound functions (hint: functions are descriptors), you can accomplish code that is roughly what you want.

因为Python中的属性查找是程序员可以控制的,所以从技术上讲,这类事情是可能的。如果您将任何值以“python”的方式编写代码(使用python社区的首选惯例和习惯用语),那么就很有可能是错误的方式来构造问题/设计。但是,如果您知道描述符如何允许您控制属性查找,以及函数如何成为绑定函数(提示:函数是描述符),那么您就可以完成大致是您想要的代码。

For a given name, there is only one object that will be looked up on a class, regardless of whether you are looking the name up on an instance of the class, or the class itself. Thus, the thing that you're looking up has to deal with the two cases, and dispatch appropriately.

对于给定的名称,无论您是在类的实例上查找名称,还是在类本身上查找名称,在类上只查找一个对象。因此,您正在查找的东西必须处理这两种情况,并适当地发送。

(Note: this isn't exactly true; if an instance has a name in its attribute namespace that collides with one in the namespace of its class, the value on the instance will win in some circumstances. But even in those circumstances, it won't become a "bound method" in the way that you probably would wish it to.)

(注:事实并非如此;如果实例的属性名称空间中有一个名称,该名称空间与其类的名称空间中的名称发生冲突,那么在某些情况下实例上的值将获胜。但即使在这种情况下,它也不会以你可能希望的方式成为一种“绑定方法”。

I don't recommend designing your program using a technique such as this, but the following will do roughly what you asked. Understanding how this works requires a relatively deep understanding of python as a language.

我不建议您使用这样的技术来设计您的程序,但是下面将大致完成您所要求的工作。要理解这是如何工作的,需要对python作为一种语言有一个相对深入的理解。

class StaticOrInstanceDescriptor(object):

    def __get__(self, cls, inst):
        if cls is None:
            return self.instance.__get__(self)
        else:
            return self.static

    def __init__(self, static):
        self.static = static

    def instance(self, instance):
        self.instance = instance
        return self


class MyClass(object):

    @StaticOrInstanceDescriptor
    def foo():
        return 'static method'

    @foo.instance
    def foo(self):
        return 'public method'

obj = MyClass()
print(obj.foo())
print(MyClass.foo())

which does print out:

并打印出:

% python /tmp/sandbox.py
static method
public method

#3


3  

While it's not strictly possible to do, as rightly pointed out, you could always "fake" it by redefining the method on instantiation, like this:

正如正确指出的那样,虽然严格来说不可能做到这一点,但您可以通过重新定义实例化方法来“伪造”它,如下所示:

class YourClass(object):

    def __init__(self):
        self.foo = self._instance_foo

    @staticmethod
    def foo()
        print "Static!"

    def _instance_foo(self):
        print "Instance!"

which would produce the desired result:

这将产生预期的结果:

>>> YourClass.foo()
Static!
>>> your_instance = YourClass()
>>> your_instance.foo()
Instance!

#4


1  

Ended up here from google so thought I would post my solution to this "problem"...

结束于谷歌,所以我想把我的解决方案发布到这个“问题”上……

class Test():
    def test_method(self=None):
        if self is None:
            print("static bit")
        else:
            print("instance bit")

This way you can use the method like a static method or like an instance method.

通过这种方法,您可以使用像静态方法或实例方法这样的方法。

#5


0  

I'm really ignorant in Python when it comes to these types of questions but I agree with a number of the sentiments in the answers so far that it's not native and you should not try.

当涉及到这些类型的问题时,我在Python中真的很无知,但是我同意到目前为止答案中的一些观点,因为它不是原生的,你不应该尝试。

So, now for something completely different. I just mocked up the following for a similar problem I have:

所以,现在来看看完全不同的东西。我刚刚模拟了一个类似的问题:

class Foo(object):
    def __init__(self, name):
        self.name = name

    @staticmethod
    def get_norm_name(name):
        # Normalize name
        return name.strip().lower()

    def norm_name(self):
        return Foo.get_norm_name(self.name)

>>> Foo.get_norm_name('Robert')
'robert'

>>> f = Foo('Robert')
>>> f.norm_name()
'robert'

The method names are different, which you were trying to avoid, but I think they're close enough. Again, not sure if this is Pythonic, but it seems easy to understand.

方法名是不同的,这是您试图避免的,但是我认为它们非常接近。同样,不确定这是不是python的,但是看起来很容易理解。