Statik yöntem ve sınıf yöntemi arasındaki fark


3579

Bir fonksiyonu süslenmiş arasındaki fark nedir @staticmethodve bir dekore @classmethod?


11
temizlik için python'da modül seviyesi işlev gördüğünden statik yöntemler bazen daha iyidir. Bir modül fonksiyonu ile sadece ihtiyacınız olan fonksiyonu almak ve gereksiz "." sözdizimi (Sana Objective-C bakıyorum). sınıf yöntemleri, "fabrika düzeni" fonksiyonlarını oluşturmak için polimorfizmle kombinasyon halinde kullanılabildiklerinden daha fazla kullanılır. Bunun nedeni, sınıf yöntemlerinin sınıfı örtük bir parametre olarak almasıdır.
FistOfFury

27
tl; dr >> normal yöntemlerle karşılaştırıldığında, statik yöntemlere ve sınıf yöntemlerine sınıf kullanılarak da erişilebilir, ancak sınıf yöntemlerinin aksine, statik yöntemler kalıtım yoluyla değiştirilemez.
imsrgadich

4
Raymond Hettinger tarafından konu hakkında ilgili konuşma: youtube.com/watch?v=HTLu2DFOdTg
moooeeeep

daha kesin youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 alternatif kurucular için yalnızca sınıf yöntemine ihtiyacınız vardır. Aksi takdirde, staticmethod kullanabilir ve classmethod'daki gibi cl'ler yerine herhangi bir sınıf niteliğine (. / Dot aracılığıyla) bir şekilde daha bilgilendirici gerçek "CLASSNAME" ile erişebilirsiniz
Robert Nowak

Yanıtlar:


3137

Çağrı imzaları fark farkını: Belki örnek kod biraz yardımcı olacaktır foo, class_foove 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()

Aşağıda, bir nesne örneğinin bir yöntemi çağırmasının olağan yolu verilmiştir. Nesne örneği, adolaylı olarak ilk argüman olarak geçirilir.

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

Classmethods ile , bir nesne, örneğin sınıf dolaylı yerine ilk bağımsız değişken olarak geçirilir self.

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

class_fooSınıfı kullanarak da arayabilirsiniz . Aslında, bir şeyi bir sınıf yöntemi olarak tanımlarsanız, bunun nedeni büyük olasılıkla onu bir sınıf örneğinden ziyade sınıftan çağırmak istemenizdir. A.foo(1)bir TypeError oluşturur, ancak iyi A.class_foo(1)çalışır:

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

İnsanların sınıf yöntemleri için buldukları bir kullanım, kalıtımsal alternatif kurucular yaratmaktır .


Statik yöntemlerle , ne self(nesne örneği) ne de cls(sınıf) ilk argüman olarak örtük olarak geçirilmez. Bunları bir örnek veya sınıftan çağırabilmeniz dışında düz işlevler gibi davranırlar:

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

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

Staticmethods, bir sınıfla sınıfla mantıksal bağlantısı olan işlevleri gruplandırmak için kullanılır.


foosadece bir işlevdir, ancak çağırdığınızda işlevi a.fooalmayacaksınız, işlevin ailk argümanı olarak bağlı nesne örneği ile işlevin "kısmen uygulanmış" sürümünü alırsınız . foo2 bağımsız değişken, a.fooyalnızca 1 bağımsız değişken bekler.

abağlı foo. Aşağıdaki "bağlı" terimi ile kastedilen:

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

İle a.class_foo, abağlı değil class_foo, daha ziyade sınıf Abağlı class_foo.

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

Burada, bir statik yöntemle, bir yöntem olmasına rağmen, a.static_fooyalnızca bağımsız bir işlev olmadan bağımsız bir 'ole işlevi döndürür. static_foo1 bağımsız değişken ve a.static_foo1 bağımsız değişken de bekliyor.

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

Ve elbette aynı şey static_foosınıfla aradığınızda da olur A.

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

182
Statik metodu kullanmak için gereken şeyin ne olduğunu anlamıyorum. basit bir sınıf dışı işlev kullanabiliriz.
Alcott

372
@Alcott: Bir işlevi bir sınıfa taşımak isteyebilirsiniz çünkü mantıksal olarak sınıfa aittir. Python kaynak kodunda (örn. Çoklu işlem, kaplumbağa, dağıtım paketleri), modülün ad alanından tek alt çizgi "özel" işlevlerini "gizlemek" için kullanılır. Bununla birlikte, kullanımı sadece birkaç modülde yoğunlaşmıştır - belki de esasen stilistik bir şey olduğunun bir göstergesi. Buna bir örnek @staticmethodbulamamam da, alt sınıflar tarafından geçersiz kılınarak kodunuzu düzenlemenize yardımcı olabilir. Bu olmadan, modül ad alanında fonksiyonun değişkenleri olurdu.
unutbu

15
... örnek, sınıf veya statik yöntemlerin nerede ve neden kullanılacağıyla ilgili bazı açıklamalarla birlikte . Bu konuda tek bir kelime bile etmediniz, fakat OP de bunu sormadı.
MestreLion

106
@Alcott: unutbu'nun dediği gibi, statik yöntemler bir organizasyon / stilistik özelliktir. Bazen bir modül çok sayıda sınıfa sahiptir ve bazı yardımcı fonksiyonlar mantıklı olarak belirli bir sınıfa bağlıdır, diğerlerine değil, bu nedenle modülü birçok "serbest fonksiyon" ile "kirletmemek" mantıklıdır ve statik kullanmak daha iyidir sadece "ilgili" olduğunu göstermek için kod sınıfları ve işlev defs zayıf tarzı güvenmek yerine yöntem
MestreLion

4
Bir kullanım daha @staticmethod- ritmi kaldırmak için kullanabilirsiniz. Python'da bir programlama dili uyguluyorum - kütüphane tanımlı işlevler execute, kullanıcı tanımlı işlevlerin örnek bağımsız değişkenler (işlev gövdesi) gerektirdiği statik bir yöntem kullanır . Bu dekoratör, PyCharm denetçisindeki "kullanılmayan parametre öz" uyarılarını ortadan kaldırır.
tehwalrus

798

Bir durukyöntem Üzerinde denilen sınıf veya örneği hakkında hiçbir şey bilmeyen bir yöntemdir. Sadece geçirilen argümanları alır, üstü kapalı ilk argüman yoktur. Temelde Python'da işe yaramaz - statik yöntem yerine sadece bir modül işlevi kullanabilirsiniz.

Bir classmethod , diğer taraftan, birinci bağımsız değişken olarak, bu ilgili denilen sınıfı ya da üzerinde denilen örneğinin sınıfı geçirilen bir yöntemdir. Bu, yöntemin sınıf için bir fabrika olmasını istediğinizde yararlıdır: ilk argüman olarak çağrıldığı gerçek sınıfı aldığından, alt sınıflar dahil olsa bile her zaman doğru sınıfı başlatabilirsiniz. Örneğin dict.fromkeys(), bir sınıf yönteminin , bir alt sınıfta çağrıldığında alt sınıfın bir örneğini nasıl verdiğini gözlemleyin :

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

714
Statik yöntem işe yaramaz - sınıfa erişim gerektirmediğini belirtirken, bir işlevi sınıfa (mantıksal olarak oraya ait olduğu için) koymanın bir yoludur.
Tony Meyer

136
Dolayısıyla sadece 'temelde' yararsızdır. Bu tür bir organizasyon ve bağımlılık enjeksiyonu, statik yöntemlerin geçerli kullanımlarıdır, ancak Java gibi sınıflar değil, modüller Python'daki kod organizasyonunun temel unsurları olduğundan, bunların kullanımı ve kullanışlılığı nadirdir.
Thomas Wouters

40
Sınıf veya onun örnekleriyle hiçbir ilgisi olmadığında, sınıf içindeki bir yöntemi tanımlamanın mantıklı tarafı nedir?
Ben James

106
Belki miras uğruna? Statik yöntemler, örnek yöntemleri ve sınıf yöntemleri gibi miras alınabilir ve geçersiz kılınabilir ve arama beklendiği gibi çalışır (Java'da olduğu gibi). Statik yöntemler, ister sınıfta ister örnekte çağrılsın, statik olarak gerçekten çözümlenmez, bu nedenle sınıf ve statik yöntemler arasındaki tek fark, örtük ilk argümandır.
haridsv

80
Ayrıca daha temiz bir ad alanı oluştururlar ve işlevin sınıfla bir ilgisi olduğunu anlamayı kolaylaştırırlar.
Imbrondir

149

Temel olarak @classmethod, ilk argümanı çağrıldığı sınıf olan (sınıf örneği yerine), @staticmethodherhangi bir örtülü argüman içermeyen bir yöntem yapar .


103

Resmi python belgeleri:

@classmethod

Sınıf yöntemi, tıpkı bir örnek yönteminin örneği alması gibi, sınıfı örtük ilk bağımsız değişken olarak alır. Bir sınıf yöntemi bildirmek için şu deyimi kullanın:

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

@classmethodForm bir işlevdir dekoratör - fonksiyonu tanımların açıklamasına bakın Fonksiyon tanımları ayrıntılar için.

Sınıfta (örneğin C.f()) veya bir örnekte (örneğin) çağrılabilir C().f(). Örnek, sınıfı dışında yok sayılır. Türetilmiş bir sınıf için bir sınıf yöntemi çağrılırsa, türetilmiş sınıf nesnesi, zımni ilk bağımsız değişken olarak iletilir.

Sınıf yöntemleri C ++ veya Java statik yöntemlerinden farklıdır. Bunları istiyorsanız, staticmethod()bu bölüme bakın.

@staticmethod

Statik bir yöntem, örtük bir ilk argüman almaz. Statik bir yöntem bildirmek için şu deyimi kullanın:

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

@staticmethodForm bir işlevdir dekoratör - fonksiyonu tanımların açıklamasına bakın Fonksiyon tanımları ayrıntılar için.

Sınıfta (örneğin C.f()) veya bir örnekte (örneğin) çağrılabilir C().f(). Örnek, sınıfı dışında yok sayılır.

Python'daki statik yöntemler Java veya C ++ 'da bulunanlara benzer. Daha gelişmiş bir konsept classmethod()için bu bölüme bakın.


Dokümanlarda bir hata yok mu? Statik yöntemde olmamalıdır: "Örnek ve sınıfın ikisi de yoksayılır." "Örnek sınıfı dışında yok sayılır."
mirek

Bu bir kes ve yapıştır hatası olabilir, ancak kesinlikle, sınıfı görmezden gelirseniz bir sınıfta yöntem çağıramazsınız.
Aaron Bentley

76

İşte bu soru hakkında kısa bir makale

@staticmethod işlevi, bir sınıf içinde tanımlanan bir işlevden başka bir şey değildir. İlk önce sınıfı somutlaştırmadan çağrılabilir. Tanımı kalıtım yoluyla değişmez.

@classmethod işlevi sınıfı somutlaştırmadan da çağrılabilir, ancak tanımı kalıtım yoluyla Üst sınıf değil Alt sınıftan sonra gelir. Çünkü @classmethod işlevi için ilk argüman her zaman cls (sınıf) olmalıdır.


1
Yani bu bir staticmethod kullanarak her zaman Parent sınıfına bağlı olduğum ve classmethod ile classmethod (bu durumda alt sınıf) bildirdiğim sınıfa bağlı olduğum anlamına mı geliyor?
Mohan Gulati

7
Hayır. Statik yöntem kullanarak hiç bağlanmazsınız; örtük ilk parametre yoktur. Classmethod kullanarak, yöntemi çağırdığınız sınıfı (doğrudan bir sınıfta çağırdıysanız) veya yöntemi çağırdığınız örneğin (örneğin bir örnekte çağırdıysanız) örtük ilk parametresini alırsınız.
Matt Anderson

6
İlk argüman olarak bir sınıfa sahip olarak, sınıf yöntemlerinin diğer sınıf özniteliklerine ve yöntemlerine doğrudan erişimi olduğunu göstermek için biraz genişletilebilirken, statik yöntemlerin (bunun için MyClass.attr
kodunu yazmaları

"Tanımı kalıtım yoluyla değişmez." Python'da bir anlam ifade etmiyorsa, statik yöntemi gayet iyi bir şekilde geçersiz kılabilirsiniz.
cz

68

@Staticmethod veya @classmethod kullanıp kullanmayacağınıza karar vermek için yönteminizin içine bakmanız gerekir. Yönteminiz sınıfınızdaki diğer değişkenlere / yöntemlere erişiyorsa @classmethod kullanın . Öte yandan, yönteminiz sınıfın başka bir yerine dokunmuyorsa, @staticmethod kullanın.

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

classmethod ve cls._counter ve staticmethod ve Apple._counter'a karşı avantajı ne olurdu
Robert Nowak

1
cls._countercls._counterkod farklı bir sınıfa yerleştirilse veya sınıf adı değiştirilse bile yine de olur. sınıfa Apple._counterözeldir Apple; farklı bir sınıf için veya sınıf adı değiştirildiğinde, referans verilen sınıfı değiştirmeniz gerekir.
kiamlaluno

53

Python'da @staticmethod ve @classmethod arasındaki fark nedir?

Çeşitli yöntem türlerinin imzalarını gösteren ve her birini açıklamak için bir doktora sağlayan bu sahte kod gibi Python kodunu görmüş olabilirsiniz:

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.
        '''

Normal Örnek Yöntemi

Önce açıklayacağım a_normal_instance_method. Buna tam olarak " örnek yöntemi " denir " . Bir örnek yöntemi kullanıldığında, kısmi bir işlev olarak kullanılır (kaynak kodunda görüntülendiğinde tüm değerler için tanımlanmış toplam işlevin aksine), yani kullanıldığında, bağımsız değişkenlerin ilki öntanımlı nesne, verilen tüm özellikleri ile birlikte. Kendisine bağlı nesnenin örneğine sahiptir ve nesnenin bir örneğinden çağrılmalıdır. Genellikle, örneğin çeşitli özniteliklerine erişir.

Örneğin, bu bir dizenin örneğidir:

', '

joinBu dizede, başka bir yinelenebilir öğeye katılmak için örnek yöntemini kullanırsak , yinelenebilir listenin bir işlevi olmasının yanı sıra, örneğin bir işlevi de oldukça açıktır ['a', 'b', 'c']:

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

Bağlı yöntemler

Örnek yöntemleri daha sonra kullanılmak üzere noktalı bir arama ile bağlanabilir.

Örneğin, bu str.joinyöntem ':'örneğe bağlanır :

>>> join_with_colons = ':'.join 

Ve daha sonra bunu ilk argümana zaten bağlı olan bir işlev olarak kullanabiliriz. Bu şekilde, örnek üzerinde kısmi bir işlev gibi çalışır:

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

Statik Yöntem

Statik yöntem yok değil bir argüman olarak örneğini almak.

Bir modül seviyesi fonksiyonuna çok benzer.

Ancak, modül düzeyinde bir işlev modülde yaşamalı ve özel olarak kullanıldığı diğer yerlere aktarılmalıdır.

Ancak nesneye bağlıysa, içe aktarma ve miras yoluyla da nesneyi rahatlıkla izleyecektir.

Statik bir yöntem örneği str.maketrans, stringPython 3'teki modülden taşınmıştır str.translate. Aşağıda gösterildiği gibi, bir dizenin bir örneğinden kullanıldığında oldukça aptalca görünmektedir, ancak işlevi stringmodülden içe aktarmak oldukça sakıncadır ve onu olduğu gibi sınıftan çağırabilmek güzeldir.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}

Python 2'de, giderek daha az kullanışlı dize modülünden bu işlevi içe aktarmanız gerekir:

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

Sınıf Yöntemi

Sınıf yöntemi, örtük bir ilk bağımsız değişken alması nedeniyle bir örnek yöntemine benzer, ancak örneği almak yerine sınıfı alır. Bunlar genellikle daha iyi semantik kullanım için alternatif kurucular olarak kullanılır ve kalıtımı destekler.

Yerleşik bir sınıf yönteminin en kanonik örneği dict.fromkeys. Alternatif bir dict kurucu olarak kullanılır, (anahtarlarınızın ne olduğunu bildiğinizde ve onlar için varsayılan bir değer istediğinizde çok uygundur.)

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

Alt sınıfı dikte ettiğimizde, alt sınıfın bir örneğini oluşturan aynı yapıcıyı kullanabiliriz.

>>> 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'>

Bkz pandalar kaynak kodunu alternatif inşaatçı diğer benzer örnekler için ve ayrıca resmi Python belgelerine bakın classmethodve staticmethod.


43

C ++ ve sonra Java ve sonra Python ile programlama dilini öğrenmeye başladım ve bu soru, her birinin basit kullanımını anlayana kadar beni de rahatsız etti.

Sınıf Metodu: Java ve C ++ 'dan farklı olarak Python'da yapıcı aşırı yüklemesi yoktur. Ve bunu başarmak için kullanabilirsiniz classmethod. Aşağıdaki örnek bunu açıklayacaktır

Personİki argüman alan first_nameve last_nameörneğini yaratan bir sınıfımız olduğunu düşünelim Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Eğer sadece bir, sadece tek bir ad kullanarak bir sınıf oluşturmak için gereken yere Şimdi, eğer gereksinim geliyor first_name, sen olamaz Python böyle bir şey yapmak.

Bir nesne (örnek) oluşturmaya çalıştığınızda bu size bir hata verecektir.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

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

Ancak, @classmethodaşağıda belirtilenleri kullanarak aynı şeyi başarabilirsiniz

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Statik Yöntem: Bu oldukça basittir, örneğe veya sınıfa bağlı değildir ve bunu sınıf adını kullanarak arayabilirsiniz.

Diyelim ki yukarıdaki örnekte first_name20 karakteri geçmemesi gereken bir doğrulamaya ihtiyacınız var , bunu yapabilirsiniz.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

ve sadece class name

Person.validate_name("Gaurang Shah")

2
Bu eski bir gönderi, ancak sınıf yöntemi def __init__(self, first_name, last_name="")yerine bir veya iki argümanı kabul eden yapıcıya ulaşmanın daha pythonic yolu get_person. Ayrıca sonuç bu durumda tamamen aynı olacaktır.
akarilimano

31

Bence daha iyi bir soru "@classmethod vs @staticmethod ne zaman kullanılır?"

@classmethod, sınıf tanımıyla ilişkili özel üyelere kolay erişim sağlar. bu, tek tektonlar veya oluşturulan nesnelerin örnek sayısını kontrol eden fabrika sınıfları yapmanın harika bir yoludur.

@staticmethod marjinal performans artışları sağlıyor, ancak sınıf içinde bağımsız bir işlev olarak elde edilemeyen bir sınıf içinde statik bir yöntemin verimli bir şekilde kullanılmasını henüz görmedim.


31

python 2.4'e @decorators eklendi. python <2.4 kullanıyorsanız classmethod () ve staticmethod () işlevini kullanabilirsiniz.

Örneğin, bir fabrika yöntemi (hangi argümana sahip olduğuna bağlı olarak sınıfın farklı bir uygulamasının bir örneğini döndüren bir işlev) oluşturmak istiyorsanız, şöyle bir şey yapabilirsiniz:

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)

Ayrıca, bunun bir sınıf yöntemi ve statik yöntem kullanmak için iyi bir örnek olduğunu gözlemleyin, Statik yöntem açıkça sınıfa aittir, çünkü Küme sınıfını dahili olarak kullanır. Sınıf yöntemi yalnızca sınıf hakkında bilgiye ihtiyaç duyar ve nesnenin hiçbir örneğine sahip değildir.

_is_cluster_forYöntemi bir sınıf yöntemi haline getirmenin diğer bir yararı, bir alt sınıfın uygulamayı değiştirmeye karar verebilmesidir, belki de oldukça genel olduğundan ve birden fazla küme türünü işleyebildiğinden, sınıfın adını kontrol etmek yeterli olmaz.


28

Statik Yöntemler:

  • Öz argüman olmadan basit fonksiyonlar.
  • Sınıf nitelikleri üzerinde çalışma; örnek özelliklerinde değil.
  • Hem sınıf hem de örnek üzerinden çağrılabilir.
  • Yerleşik staticmethod () işlevi bunları oluşturmak için kullanılır.

Statik Yöntemlerin Faydaları:

  • Sınıf adındaki işlev adını yerelleştirir
  • İşlev kodunu kullanıldığı yere yaklaştırır
  • Her yöntemin özel olarak içe aktarılması gerekmediğinden modül düzeyinde işlevlere karşı içe aktarma daha uygun

    @staticmethod
    def some_static_method(*args, **kwds):
        pass

Sınıf Yöntemleri:

  • Sınıf adı olarak ilk bağımsız değişkene sahip işlevler.
  • Hem sınıf hem de örnek üzerinden çağrılabilir.
  • Bunlar classmethod yerleşik işleviyle oluşturulur.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass

22

@staticmethodyöntem tanımlayıcı olarak varsayılan işlevi devre dışı bırakır. classmethod, işlevinizi birinci bağımsız değişken olarak sahip olan sınıfa başvuru ileten çağrılabilir bir kapta sarar:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

Nitekim, classmethodbir çalışma zamanı yükü vardır, ancak sahip olan sınıfa erişmeyi mümkün kılar. Alternatif olarak, bir metasınıf kullanmanızı ve bu metasınıfın sınıf yöntemlerini koymanızı öneririm:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

1
Bunun için hemen gerçekleşen bir metasınıfın olası bir dezavantajı, sınıf yöntemini doğrudan bir örnek üzerinde çağıramayacağınızdır. c = C(); c.foo()AttributeError'ı kaldırır, yapmanız gerekir type(c).foo(). Bu da bir özellik olarak düşünülebilir - neden düşünmek istediğinizi düşünemiyorum.
Aaron Hall

20

Python'da statik, sınıf veya soyut yöntemlerin nasıl kullanılacağına dair kesin kılavuz, bu konu için iyi bir bağlantıdır ve aşağıdaki gibi özetleyin.

@staticmethodişlev, bir sınıf içinde tanımlanan bir işlevden başka bir şey değildir. İlk önce sınıfı somutlaştırmadan çağrılabilir. Tanımı kalıtım yoluyla değişmez.

  • Python'un nesne için bir bağlı yöntem başlatması gerekmez.
  • Kodun okunabilirliğini kolaylaştırır ve nesnenin durumuna bağlı değildir;

@classmethodişlevi sınıfı somutlaştırmadan da çağrılabilir, ancak tanımını kalıtım yoluyla üst sınıf değil Alt sınıf izler, alt sınıf tarafından geçersiz kılınabilir. Çünkü @classmethodişlev için ilk argüman her zaman cls (sınıf) olmalıdır.

  • Fabrika yöntemleriÖrneğin bir tür ön işleme kullanarak bir sınıf için örnek oluşturmak için kullanılan .
  • Statik yöntemler çağıran statik yöntemler : statik yöntemleri birkaç statik yöntemde bölerseniz, sınıf adını sabit kodlamamalısınız, ancak sınıf yöntemlerini kullanmalısınız

Thanks @zangw - statik fonksiyonun kalıtsal değişmezliği gibi görünüyor önemli fark
hard_working_ant

18

Sadece ilk argüman farklıdır :

  • normal yöntem: mevcut nesne otomatik olarak (ek) ilk bağımsız değişken olarak iletilirse
  • classmethod: geçerli nesnenin sınıfı otomatik olarak (ek) yumruk argümanı olarak iletilir
  • staticmethod: otomatik olarak fazladan argüman iletilmez. İşleve geçirdiğiniz şey elde ettiğiniz şeydir.

Daha ayrıntılı olarak ...

normal yöntem

Bir nesnenin yöntemi çağrıldığında, otomatik selfolarak ilk argümanı olarak ekstra bir argüman verilir . Yani, yöntem

def f(self, x, y)

2 argümanla çağrılmalıdır. selfotomatik olarak iletilir ve nesnenin kendisidir .

sınıf yöntemi

Yöntem süslendiğinde

@classmethod
def f(cls, x, y)

otomatik olarak sağlanan argüman değil self , fakat sınıf self .

statik yöntem

Yöntem süslendiğinde

@staticmethod
def f(x, y)

yönteme hiçbir otomatik argüman verilmez . Sadece çağrıldığı parametreler verilir.

kullanımları

  • classmethod çoğunlukla alternatif kurucular için kullanılır.
  • staticmethodnesnenin durumunu kullanmaz. Bir sınıfa harici bir işlev olabilir. Yalnızca benzer işlevlere sahip işlevleri gruplamak için sınıfın içine yerleştirilir (örneğin, Java'nın Mathsınıf statik yöntemleri gibi)
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

17

Önce @classmethod vs @staticmethod ile dekore edilmiş bir yöntem arasındaki benzerliği söyleyeyim.

Benzerlik: Her ikisi de sadece sınıf örneği yerine Sınıfın kendisinde çağrılabilir . Yani her ikisi de bir anlamda Class'ın yöntemleridir .

Fark: Bir sınıf yöntemi, sınıfın kendisini ilk argüman olarak alırken, bir statik yöntem bunu yapmaz.

Dolayısıyla statik bir yöntem, bir anlamda, Sınıfın kendisine bağlı değildir ve sadece ilgili bir işlevselliğe sahip olabileceği için orada asılı kalır.

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

11

Statik yöntem ile sınıf yöntemine ilişkin bir başka husus, kalıtımla ortaya çıkmaktadır. Aşağıdaki sınıfa sahip olduğunuzu varsayalım:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

Ve sonra bar()bir alt sınıfta geçersiz kılmak istiyorsunuz :

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Bu işe yarar, ancak artık bar()alt sınıftaki ( Foo2) uygulamanın artık bu sınıfa özgü hiçbir şeyden yararlanamayacağını unutmayın. Örneğin, diyelim ki uygulanmasında kullanmak istediğiniz Foo2adlı bir yöntem vardı :magic()Foo2bar()

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

Geçici çözüm burada aramak olurdu Foo2.magic()içinde bar()ama (adını o zaman kendini tekrar ediyorsun Foo2değişikliklerle, bunu güncellemek için hatırlamak gerekecek bar()yöntemi).

Bana göre, bu hafif bir ihlali açık / kapalı prensibinin , çünkü alınan bir karar Footüretilmiş bir sınıftaki ortak kodu yeniden düzenleme yeteneğinizi etkilemektedir (yani uzantıya daha az açıktır). Eğer bar()bir vardı classmethodbiz iyi olurdu:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

verir: In Foo2 MAGIC


7

Temel farkı bir örnek kullanarak açıklamaya çalışacağım.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - başlatmadan doğrudan statik ve sınıf yöntemlerini çağırabiliriz

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- Statik yöntem kendi yöntemini çağıramaz, ancak diğer statik ve sınıf yöntemlerini çağırabilir

3- Statik yöntem sınıfa aittir ve hiç nesne kullanmayacaktır.

4- Sınıf yöntemi bir nesneye değil, bir sınıfa bağlıdır.


ad 2: Emin misiniz? Statik yöntem sınıf yöntemini nasıl çağırabilir? Buna (sınıfına) referansı yoktur.
mirek

7

@classmethod: o sınıfın oluşturulan tüm örnekleri için paylaşılan bir global erişim oluşturmak için kullanılabilir ..... birden fazla kullanıcı tarafından bir kayıt güncelleme gibi .... Ben de singletonları oluştururken ful kullanın bulundu ..: )

@static method: ilişkilendirilmiş sınıf veya örnekle hiçbir ilgisi yok ... ancak okunabilirlik için statik yöntem kullanabilir


5

Arasındaki farkı dikkate almak isteyebilirsiniz:

Class A:
    def foo():  # no self parameter, no decorator
        pass

ve

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Bu python2 ve python3 arasında değişti:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Bu yüzden @staticmethodsadece doğrudan sınıftan çağrılan yöntemler için python3'te isteğe bağlı hale gelmiştir. Bunları hem sınıftan hem de örnekten çağırmak istiyorsanız, yine de @staticmethoddekoratörü kullanmanız gerekir .

Diğer vakalar unutbus cevabı ile iyi karşılanmıştır.


5

Sınıf yöntemi, tıpkı bir örnek yönteminin örneği alması gibi, sınıfı örtük ilk bağımsız değişken olarak alır. Sınıfın nesnesine değil sınıfa bağlı bir yöntemdir. Nesne örneğini değil, sınıfı işaret eden bir sınıf parametresini aldığından sınıfın durumuna erişebilir. Sınıfın tüm örnekleri için geçerli olacak bir sınıf durumunu değiştirebilir. Örneğin, tüm örneklere uygulanacak bir sınıf değişkenini değiştirebilir.

Öte yandan, statik yöntem sınıf yöntemlerine veya örnek yöntemlere kıyasla örtük bir ilk bağımsız değişken almaz. Sınıf durumuna erişemez veya sınıf durumunu değiştiremez. Sadece sınıfa aittir, çünkü tasarım açısından bu doğru yoldur. Ancak işlevsellik açısından, çalışma zamanında sınıfa bağlı değildir.

kılavuz olarak, statik yöntemleri yardımcı programlar olarak, sınıf yöntemlerini örneğin fabrika olarak kullanın. Veya belki bir singleton tanımlamak için. Örneklerin durumunu ve davranışını modellemek için örnek yöntemlerini kullanın.

Umarım netmişimdir!


4

Katkım @classmethod, @staticmethodbir örneğin dolaylı olarak a'yı nasıl çağırabileceği de dahil olmak üzere, ve örnek yöntemleri arasındaki farkı gösterir @staticmethod. Ancak dolaylı @staticmethodolarak bir örnekten a çağırmak yerine, onu özel yapmak daha "pitonik" olabilir. Özel bir yöntemden bir şey elde etmek burada gösterilmemiştir, ancak temel olarak aynı kavramdır.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

2

Sınıf yöntemleri, adından da anlaşılacağı gibi, nesnelerde değil sınıflarda değişiklik yapmak için kullanılır. Sınıflarda değişiklik yapmak için, sınıfları bu şekilde güncellediğiniz için sınıf özniteliklerini değiştirir (nesne özniteliklerini değil). Sınıf yöntemlerinin sınıfı (geleneksel olarak 'cls' ile gösterilen) ilk argüman olarak almasının nedeni budur.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Statik yöntemler, sınıfa bağlı olmayan işlevleri yerine getirmek için kullanılır, yani sınıf değişkenlerini okumaz veya yazmazlar. Bu nedenle, statik yöntemler sınıfları bağımsız değişken olarak almaz. Sınıfların doğrudan sınıfın amacı ile ilgili olmayan işlevleri yerine getirebilmesi için kullanılırlar.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

2

@Staticmethod kelimesini kelimesine göre analiz et farklı bilgiler sağlayarak .

Bir sınıfın normal yöntemi , örneği ilk argüman olarak alan kapalı bir dinamik yöntemdir.
Bunun aksine, bir statik yöntem örneği ilk argüman olarak almaz, buna 'statik' denir .

Statik yöntem aslında sınıf tanımının dışındaki normal bir işlevdir.
Neyse ki uygulandığı yere daha yakın durmak için sınıfa gruplandırılır ya da bulmak için gezinebilirsiniz.


2

Bence sadece Python versiyonunu veriyorum staticmethodveclassmethod aralarındaki farkı dil düzeyinde anlamaya yardımcı olmak.

Her ikisi de veri olmayan tanımlayıcılardır ( Önce tanımlayıcılara aşina iseniz bunları anlamak daha kolay olacaktır ).

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

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

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner

1

staticmethod, nesnenin, sınıfın veya miras hiyerarşisindeki üst sınıfların özniteliklerine erişemez. Doğrudan sınıfta çağrılabilir (nesne oluşturmadan).

classmethod nesnesinin niteliklerine erişemez. Ancak, miras hiyerarşisinde sınıfın ve üst sınıfların özelliklerine erişebilir. Doğrudan sınıfta çağrılabilir (nesne oluşturmadan). Nesneye çağrılırsa, erişmeyen self.<attribute(s)>ve erişmeyen normal yöntemle aynıdır.self.__class__.<attribute(s)> yalnızca .

Bir sınıfımız olduğunu düşünün b=2, bir nesne yaratacağız ve b=4bunu içinde yeniden ayarlayacağız . Staticmethod öncekinden hiçbir şeye erişemez. Classmethod .b==2sadece üzerinden erişebilir cls.b. Normal yöntem her ikisine de erişebilir: .b==4üzerinden self.bve .b==2üzerinden self.__class__.b.

KISS stilini takip edebiliriz (basit, aptalca): Statik yöntemler ve sınıf yöntemleri kullanmayın, sınıfları somutlaştırmadan kullanmayın, yalnızca nesnenin niteliklerine erişin self.attribute(s). OOP'un bu şekilde uygulandığı diller var ve bence kötü bir fikir değil. :)


Classmethods için daha önemli bir şey: class yönteminde bir özniteliği değiştirirseniz, bu sınıfın bu özniteliği açıkça ayarlamayan mevcut tüm nesneleri değiştirilmiş değere sahip olacaktır.
mirek

-4

İPython'daki diğer yöntemlerin hızlı bir şekilde kesilmesi, @staticmethodmarjinal performans kazançları (nanosaniye cinsinden) verdiğini ortaya çıkarır , ancak aksi takdirde hiçbir işlev görmüyor gibi görünüyor. Ayrıca, herhangi bir performans kazancı, yöntemin,staticmethod() derleme sırasında (bir komut dosyası çalıştırdığınızda herhangi bir kod yürütülmesinden önce gerçekleşir) .

Kod okunabilirliği açısından @staticmethod, yönteminiz nanosaniyelerin önemli olduğu iş yükleri için kullanılmadıkça kaçınırım .


7
"Aksi takdirde işlev görmüyor": kesinlikle doğru değil. Yukarıdaki tartışmaya bakınız.
Keith Pinson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.