Vraag Wat is het verschil tussen @staticmethod en @classmethod in Python?


Wat is het verschil tussen een functie gedecoreerd met @staticmethod en een versierd met @classmethod?


2671
2017-09-25 21:01


oorsprong


antwoorden:


Misschien helpt een beetje voorbeeldcode: Let op het verschil in de call signatures van foo, class_foo en static_foo:

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)

    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)"%(cls,x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)"%x    

a=A()

Hieronder ziet u de gebruikelijke manier waarop een objectinstantie een methode aanroept. De objectinstantie, a, wordt impliciet doorgegeven als het eerste argument.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

Met classmethoden, de klasse van de objectinstantie wordt impliciet doorgegeven als het eerste argument in plaats van self.

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Je kunt ook bellen class_foo de klasse gebruiken. In feite, als je definieert wat je wilt zijn een class method, het is waarschijnlijk omdat je het van plan bent om het vanuit de class te noemen in plaats van vanuit een class instantie. A.foo(1) zou een TypeError hebben geroepen, maar A.class_foo(1) werkt prima:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Een gebruik dat mensen hebben gevonden voor klassemethoden, is maken overerfbare alternatieve constructeurs.


Met statische methodes, geen van beide self (de objectinstantie) en ook niet cls (de klasse) wordt impliciet doorgegeven als het eerste argument. Ze gedragen zich als gewone functies, behalve dat je ze kunt bellen vanuit een instantie of de klas:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Statische methoden worden gebruikt om functies te groeperen die een logisch verband met een klasse hebben met de klas.


foo is slechts een functie, maar als u belt a.foo je krijgt niet alleen de functie, u krijgt een "gedeeltelijk toegepaste" versie van de functie met de objectinstantie a gebonden als het eerste argument voor de functie. foo verwacht 2 argumenten, terwijl a.foo verwacht maar 1 argument.

a is gebonden aan foo. Dat is wat wordt bedoeld met de term "gebonden" hieronder:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Met a.class_foo, a is niet gebonden aan class_foo, liever de klas A is gebonden aan class_foo.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Hier, met een statische methode, ook al is het een methode, a.static_foo komt gewoon terug een goede 'ole-functie zonder gebonden argumenten. static_foo verwacht 1 argument, en a.static_foo verwacht ook 1 argument.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

En natuurlijk gebeurt hetzelfde als je belt static_foo met de klas A in plaats daarvan.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

2316
2017-11-03 19:13



EEN staticmethod is een methode die niets weet over de klasse of instantie waarop deze is aangeroepen. Het krijgt alleen de argumenten die zijn aangenomen, geen impliciete eerste argument. Het is eigenlijk nutteloos in Python - je kunt gewoon een module-functie gebruiken in plaats van een statische methode.

EEN classmethod, aan de andere kant, is een methode die de klasse waarin deze werd aangeroepen, of de klasse van de instantie waarop deze werd aangeroepen, als eerste argument doorgeeft. Dit is handig als u wilt dat de methode een fabriek is voor de klasse: aangezien deze de eigenlijke klasse krijgt, werd deze als eerste argument aangeroepen, kunt u altijd de juiste klasse instantiëren, zelfs als er subklassen bij betrokken zijn. Observeer bijvoorbeeld hoe dict.fromkeys(), een classmethode retourneert een exemplaar van de subklasse wanneer deze wordt aangeroepen in een subklasse:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 

654
2017-09-25 21:05



Eigenlijk @classmethod maakt een methode waarvan het eerste argument de klasse is waartoe het geroepen is (in plaats van het klasse-exemplaar), @staticmethodheeft geen impliciete argumenten.


110
2017-09-25 21:07



Officiële python-documenten:

@classmethod

Een klassemethode ontvangt de klasse als   impliciete eerste argument, net als een   instantie methode ontvangt de instantie.   Gebruik dit om een ​​klassenmethode te declareren   idioom:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 

De @classmethod vorm is een functie    decorateur - zie de beschrijving van   functie definities in Functie   definities voor details.

Het kan in de klas worden genoemd   (zoals C.f()) of op een instantie   (zoals C().f()). De instantie is   genegeerd behalve voor zijn klasse. Als een   klassemethode wordt aangeroepen voor een afgeleide   klasse, het afgeleide klassenobject is   geslaagd als het impliciete eerste argument.

Class-methoden zijn anders dan C ++   of Java-statische methoden. Als je wil   die, zie staticmethod() in deze   sectie.

@staticmethod

Een statische methode ontvangt geen   impliciete eerste argument. Om een ​​te declareren   statische methode, gebruik dit idioom:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 

De @staticmethod vorm is een functie    decorateur - zie de beschrijving van   functie definities in Functie   definities voor details.

Het kan in de klas worden genoemd   (zoals C.f()) of op een instantie   (zoals C().f()). De instantie is   genegeerd behalve voor zijn klasse.

Statische methoden in Python zijn vergelijkbaar   die gevonden in Java of C ++. Voor een   geavanceerdere concept, zie    classmethod() in dit gedeelte.


76
2017-11-03 19:23



Hier is een kort artikel over deze vraag

@staticmethod-functie is niets meer dan een functie die in een klasse is gedefinieerd. Het is opvraagbaar zonder eerst de klasse in gevaar te brengen. De definitie is onveranderlijk via overerving.

@classmethod functie ook callable zonder instantiëren van de klasse, maar de definitie volgt Subklasse, niet Parent klasse, via overerving. Dat komt omdat het eerste argument voor de functie @classmethod altijd cls (klasse) moet zijn.


55
2017-11-03 19:02



Om te beslissen of u het wilt gebruiken @staticmethod of @classmethod je moet in je methode kijken. Als uw methode andere variabelen / methoden in uw klas gebruikt, gebruikt u @classmethod. Aan de andere kant, als uw methode geen andere delen van de klasse raakt, gebruik dan @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

44
2018-04-22 15:40



Wat is het verschil tussen @staticmethod en @classmethod in Python?

Je hebt mogelijk de Python-code gezien zoals deze pseudocode, die de handtekeningen van de verschillende methodes demonstreert en een docstring verschaft om elk de volgende te verklaren:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

De methode Normale instantie

Eerst zal ik het uitleggen a_normal_instance_method. Dit wordt precies een "instantie methodeWanneer een instantiemethode wordt gebruikt, wordt deze gebruikt als een gedeeltelijke functie (in tegenstelling tot een totale functie, gedefinieerd voor alle waarden wanneer deze wordt bekeken in de broncode), dat wil zeggen dat de eerste van de argumenten als vooraf gedefinieerd wordt gedefinieerd als de instantie van het object, met al zijn gegeven attributen. Het heeft de instantie van het object eraan gebonden en het moet worden aangeroepen vanuit een instantie van het object. Meestal krijgt het toegang tot verschillende attributen van het exemplaar.

Dit is bijvoorbeeld een exemplaar van een tekenreeks:

', '

als we de instantiemethode gebruiken, join op deze string, om lid te worden van een andere iterable, het is overduidelijk een functie van de instantie, naast een functie van de iterabele lijst, ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Gebonden methoden

Instantiemethoden kunnen via een gestippelde opzoeking worden gebonden voor later gebruik.

Dit bindt bijvoorbeeld de str.join methode om de ':' aanleg:

>>> join_with_colons = ':'.join 

En later kunnen we dit gebruiken als een functie waaraan al het eerste argument gebonden is. Op deze manier werkt het als een gedeeltelijke functie op de instantie:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Statische methode

De statische methode doet niet neem de instantie als een argument.

Het lijkt erg op een functie op moduleniveau.

Een module niveau functie moet echter in de module leven en speciaal worden geïmporteerd naar andere plaatsen waar het wordt gebruikt.

Als het object echter aan het object is gekoppeld, zal het het object gemakkelijk volgen via importeren en overerving.

Een voorbeeld van een statische methode is str.maketrans, verplaatst van de string module in Python 3. Het maakt een vertaaltabel geschikt voor consumptie door str.translate. Het lijkt nogal onnozel wanneer het wordt gebruikt vanaf een instantie van een tekenreeks, zoals hieronder wordt getoond, maar de functie wordt geïmporteerd vanuit de string module is nogal onhandig, en het is leuk om te kunnen bellen vanuit de klas, zoals in str.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

In python 2 moet je deze functie importeren uit de steeds minder handige stringmodule:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Klasse methode

Een klassemethode is vergelijkbaar met een instantiemethode omdat deze een impliciet eerste argument vereist, maar in plaats van de instantie te nemen, is deze klasse vereist. Vaak worden deze gebruikt als alternatieve constructeurs voor beter semantisch gebruik en het zal overerving ondersteunen.

Het meest canonieke voorbeeld van een ingebouwde klassemethode is dict.fromkeys. Het wordt gebruikt als een alternatieve constructor van dict (goed geschikt als u weet wat uw sleutels zijn en een standaardwaarde voor hen wilt).

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

Wanneer we dicteren, kunnen we dezelfde constructor gebruiken, die een instantie van de subklasse maakt.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Zie de pandas broncode voor andere vergelijkbare voorbeelden van alternatieve constructors, en zie ook de officiële documentatie van Python op classmethod en staticmethod.


38
2018-01-23 20:01



@decorators zijn toegevoegd in python 2.4 Als je python <2.4 gebruikt, kun je de methode classmethod () en staticmethod () gebruiken.

Als u bijvoorbeeld een fabrieksmethode wilt maken (een functie die een instantie van een andere implementatie van een klasse retourneert, afhankelijk van het argument dat wordt gebruikt), kunt u iets doen als:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

Merk ook op dat dit een goed voorbeeld is voor het gebruik van een klassemethode en een statische methode, De statische methode behoort duidelijk tot de klasse, omdat deze de klassecluster intern gebruikt. De classmethode heeft alleen informatie over de klasse nodig en geen instantie van het object.

Een ander voordeel van het maken van de _is_cluster_for methode een classmethode is dus een subklasse kan besluiten om de implementatie te wijzigen, misschien omdat het behoorlijk generiek is en meer dan één type cluster aankan, dus alleen het controleren van de naam van de klas zou niet genoeg zijn.


27
2018-02-24 09:32