Python'da örnek değişkenleri ve sınıf değişkenleri


121

Çalışma zamanında yalnızca bir örneğine ihtiyacım olan Python sınıflarım var, bu nedenle özniteliklere örnek başına değil, sınıf başına yalnızca bir kez sahip olmak yeterli olacaktır. Birden fazla örnek olacaksa (bu gerçekleşmeyecekse), tüm örneklerin aynı konfigürasyona sahip olması gerekir. Aşağıdaki seçeneklerden hangisinin daha iyi veya daha "deyimsel" Python olacağını merak ediyorum.

Sınıf değişkenleri:

class MyController(Controller):

  path = "something/"
  children = [AController, BController]

  def action(self, request):
    pass

Örnek değişkenler:

class MyController(Controller):

  def __init__(self):
    self.path = "something/"
    self.children = [AController, BController]

  def action(self, request):
    pass

4
Bu soruyu okuduktan ve cevabını gördükten sonra ilk sorularımdan biri, "Peki sınıf değişkenlerine nasıl erişebilirim?" Oldu. - bunun nedeni, bu noktaya kadar sadece örnek değişkenleri kullandım. Kendi soruma cevap olarak, bunu sınıf adının kendisi aracılığıyla yaparsınız, ancak teknik olarak bunu bir örnek aracılığıyla da yapabilirsiniz. İşte aynı soruyu soran herkes için okuyabileceğiniz bir bağlantı: stackoverflow.com/a/3434596/4561887
Gabriel Staples

Yanıtlar:


159

Yine de yalnızca bir örneğiniz varsa, tüm değişkenleri örnek başına yapmak en iyisidir, çünkü bunlara daha hızlı erişilecek (biraz) (sınıftan örneğe "miras" nedeniyle bir "arama" düzeyi daha az), ve bu küçük avantaja karşı tartılacak hiçbir olumsuzluk yoktur.


7
Borg modelini hiç duymadınız mı? İlk başta sadece bir örneğe sahip olmak yanlış bir yoldur.
Devin Jeanpierre

435
@Devin, evet, Borg modelini duydum, çünkü onu tanıtan benim (2001'de, cfr code.activestate.com/recipes/… ;-). Ancak basit durumlarda, hiçbir yaptırım olmaksızın sadece tek bir örneğe sahip olmanın yanlış bir tarafı yoktur.
Alex Martelli

2
@ user1767754, bunları kendiniz yapmak kolaydır python -mtimeit- ancak bunu python3.4'te yaptıktan sonra, bir intsınıf değişkenine erişmenin aslında eski iş istasyonumdaki örnek değişkeninden yaklaşık 5 ila 11 nanosaniye daha hızlı olduğunu not ediyorum - ne olduğundan emin değilim codepath bunu yapar.
Alex Martelli

45

Mike'ın ve Alex'in tavsiyelerini daha fazla yansıtıyor ve kendi rengimi ekliyor ...

Örnek niteliklerini kullanmak tipiktir ... daha deyimsel Python. Sınıf öznitelikleri, kullanım durumları spesifik olduğu için pek kullanılmaz. Aynısı statik ve sınıf yöntemleri ile "normal" yöntemler için de geçerlidir. Belirli kullanım durumlarına hitap eden özel yapılardır, aksi takdirde, Python programlamasının belirsiz bir köşesini bildiklerini göstermek isteyen sapkın bir programcı tarafından oluşturulan koddur.

Alex cevabında, bir arama seviyesinin daha az olması nedeniyle erişimin (biraz) daha hızlı olacağından söz ediyor ... Bunun nasıl çalıştığını henüz bilmeyenler için daha fazla açıklama yapmama izin verin. Değişken erişime çok benzer - arama sırası şu şekildedir:

  1. yerliler
  2. nonlocals
  3. globaller
  4. Yerleşik ins

Öznitelik erişimi için sıra şu şekildedir:

  1. örnek
  2. sınıf
  3. MRO tarafından belirlenen temel sınıflar (yöntem çözümleme sırası)

Her iki teknik de "içten dışa" bir şekilde çalışır, yani çoğu yerel nesneler önce kontrol edilir, ardından dış katmanlar sırayla kontrol edilir.

Yukarıdaki örneğinizde, pathözelliği aradığınızı varsayalım. self.pathPython, " " gibi bir referansla karşılaştığında , bir eşleşme için önce örnek özniteliklerine bakacaktır. Başarısız olduğunda, nesnenin somutlaştırıldığı sınıfı kontrol eder. Son olarak, temel sınıfları arayacaktır. Alex'in belirttiği gibi, özniteliğiniz örnekte bulunursa, başka bir yere bakmasına gerek yoktur, dolayısıyla biraz zaman tasarrufu sağlarsınız.

Ancak, sınıf özniteliklerinde ısrar ediyorsanız, bu fazladan aramaya ihtiyacınız var. Veya , diğer alternatifiniz nesneye örnek yerine sınıf aracılığıyla başvurmaktır, örneğin MyController.pathyerine self.path. Bu, ertelenmiş aramadan geçecek doğrudan bir aramadır, ancak aşağıda alex'in bahsettiği gibi, bu global bir değişkendir, yani kurtaracağınızı düşündüğünüz kısmı kaybedersiniz ([global] sınıf adına yerel bir referans oluşturmadıkça ).

Sonuç olarak, çoğu zaman örnek niteliklerini kullanmanız gerekir. Ancak, sınıf niteliğinin iş için doğru araç olduğu durumlar olacaktır. Her ikisini de aynı anda kullanan kod en fazla özen gerektirir, çünkü kullanmak selfsize yalnızca örnek öznitelik nesnesini ve aynı adın sınıf özniteliğine gölge erişimini sağlar. Bu durumda, özniteliğe başvurmak için sınıf adına göre erişmeyi kullanmanız gerekir .


@wescpy, ancak MyControllergloballerde aranır, bu nedenle toplam maliyet , bir örnek değişkeninin olduğu self.pathyerden daha yüksektir path(çünkü yöntem == süper hızlı arama selfiçin yereldir ).
Alex Martelli

ah, doğru. İyi yakalama. Sanırım tek geçici çözüm yerel bir referans oluşturmaktır ... bu noktada, buna gerçekten değmez.
wescpy

24

Şüpheniz olduğunda, muhtemelen bir örnek niteliği istersiniz.

Sınıf nitelikleri en iyi anlam ifade ettikleri özel durumlar için ayrılmıştır. Tek yaygın kullanım durumu yöntemlerdir. Örneklerin bilmesi gereken salt okunur sabitler için sınıf özniteliklerinin kullanılması alışılmadık bir durum değildir (bunun tek yararı , sınıf dışından da erişim istiyorsanız ), ancak bunlarda herhangi bir durumu saklamak konusunda kesinlikle dikkatli olmalısınız. , nadiren istediğiniz şey budur. Yalnızca bir örneğiniz olsa bile, sınıfı diğer herhangi bir örneğiniz gibi yazmalısınız, bu genellikle örnek özniteliklerini kullanmak anlamına gelir.


1
Sınıf değişkenleri bir tür salt okunur sabitlerdir. Python sabitleri tanımlamama izin verseydi, onu sabitler olarak yazardım.
deamon

1
@deamon, sabitlerimi sınıf tanımlarının tamamen dışına koyma olasılığım biraz daha yüksek ve hepsini büyük harflerle adlandırıyorum. Onları sınıfa koymak da sorun değil. Onları örnek öznitelikleri yapmak hiçbir şeye zarar vermez, ancak biraz tuhaf olabilir. Bunun, topluluğun seçeneklerden birinin çok fazla arkasında kaldığı bir konu olduğunu düşünmüyorum.
Mike Graham

@MikeGraham FWIW, Google'ın Python Stil Kılavuzu , sınıf değişkenleri lehine küresel değişkenlerden kaçınmayı önerir. Yine de istisnalar var.
Dennis

İşte Google'ın Python Stil Kılavuzuna yeni bir bağlantı . Şimdi basitçe yazılmış: avoid global variablesve onların tanımı, küresel değişkenler aynı zamanda sınıf öznitelikleri olarak bildirilen değişkenlerdir. Bununla birlikte, Python'un kendi stil kılavuzu ( PEP-8 ), bu tür sorular için ilk gidilecek yer olmalıdır. O zaman kendi zihniniz tercih edilen araç olmalıdır (örneğin, Google'dan da fikir edinebilirsiniz).
colidyre

4

Python'daki sınıf değişkenlerine erişim performansında da aynı soru - buradaki kod @Edward Loper'dan uyarlanmıştır

Yerel Değişkenler erişilmesi en hızlı olanlardır, Modül Değişkenleri ile hemen hemen aynıdır, ardından Sınıf Değişkenleri ve ardından Örnek Değişkenleri gelir.

Değişkenlere erişebileceğiniz 4 kapsam vardır:

  1. Örnek Değişkenleri (self.varname)
  2. Sınıf Değişkenleri (Sınıfadı.varname)
  3. Modül Değişkenleri (VARNAME)
  4. Yerel Değişkenler (varname)

Test:

import timeit

setup='''
XGLOBAL= 5
class A:
    xclass = 5
    def __init__(self):
        self.xinstance = 5
    def f1(self):
        xlocal = 5
        x = self.xinstance
    def f2(self):
        xlocal = 5
        x = A.xclass
    def f3(self):
        xlocal = 5
        x = XGLOBAL
    def f4(self):
        xlocal = 5
        x = xlocal
a = A()
'''
print('access via instance variable: %.3f' % timeit.timeit('a.f1()', setup=setup, number=300000000) )
print('access via class variable: %.3f' % timeit.timeit('a.f2()', setup=setup, number=300000000) )
print('access via module variable: %.3f' % timeit.timeit('a.f3()', setup=setup, number=300000000) )
print('access via local variable: %.3f' % timeit.timeit('a.f4()', setup=setup, number=300000000) )

Sonuç:

access via instance variable: 93.456
access via class variable: 82.169
access via module variable: 72.634
access via local variable: 72.199
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.