문제

I hoped this would work:

class A(object):

    @classmethod
    def do_it(cls, spam, eggs):
        if spam in A.ways_to_do_it:
            A.ways_to_do_it[spam](eggs)
        super(A, cls).do_it(spam, eggs)

    @staticmethod
    def do_it_somehow(eggs):
        ...

    @staticmethod
    def do_it_another_way(eggs):
        ...

    ways_to_do_it = {
        'somehow': do_it_somehow,
        'another_way': do_it_another_way,
    }

But it raises TypeError: 'staticmethod' object is not callable. I wanted to inspect staticmethod to find out something, but it's a built in. I hope that it's clear what I want to achieve here.

Do you have any ideas how to do it nicely? I know that making these @staticmethods global would solve the problem, but that would be a mess in my module.

P. S. do_it_somehow and do_it_another_way will be called from A.do_it only.

도움이 되었습니까?

해결책

Python has a concept of descriptor objects, which are objects having at least the __get__ method. These objects behave differently when retrieved from a class, or an instance, as attributes (their __get__ method is called.)

The @staticmethod decorator transforms the subsequent function declaration in a descriptor that has the static method behavior - but said behavior will only be available when retrieving the object as a class attribute. The code above makes a direct reference to the object as it is, instead.

Since it is you also have other (class) methods for your dictionary to work, you'd better retrieve yoiur desired methods after class creation, so that each method is retrieved via descriptor protocol:

class A(object):

    @classmethod
    def do_it(cls, spam, eggs):
        if spam in A.ways_to_do_it:
            A.ways_to_do_it[spam](eggs)
        super(A, cls).do_it(spam, eggs)

    @staticmetod
    def do_it_somehow(eggs):
        ...

    @staticmetod
    def do_it_another_way(eggs):
        ...

A.ways_to_do_it = {
        'somehow': A.do_it_somehow,
        'another_way': A.do_it_another_way,
    }

You could retrieve your static method before class creation, calling do_it_another_way.__get__(object, None) - since it does not need the class reference (but needs a valid class as first parameter nonetheless). But if you want your dictionary to also point to the classmethods defined, they definitely have to be fetched after class creation: there is no way Python can create you a "bound class method" before class creation.

Creating other direct references to the class/static methods inside the class body works:

class A(object):
   @staticmethod
   def foo(): ...

   bar = foo

because in this way, bar would also be fetched through the descriptor protocol. But since you have an indirect dictionary, you have to take care of the descriptor __get__ call yourself.

Check http://docs.python.org/2/reference/datamodel.html#implementing-descriptors for more information. (This is what the classmethod, staticmethod decorators do, since you also wanted to know).

다른 팁

Try it like this:

class A(object):

    @classmethod
    def do_it(cls, spam, eggs):
        if spam in A.ways_to_do_it:
            A.ways_to_do_it[spam](eggs)
        super(A, cls).do_it(spam, eggs)

    @staticmethod
    def do_it_somehow(eggs):
        ...

    @staticmethod
    def do_it_another_way(eggs):
        ...

A.ways_to_do_it = {
    'somehow': A.do_it_somehow,
    'another_way': A.do_it_another_way,
}

It gets tricky to reference a class before you've completed construction of the class, so it's simplest to add stuff to it right after the end of the class definition.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top