Örnek değişkenleri Python'da bir sınıfta Yok olarak bildirmek iyi bir uygulama mıdır?


68

Aşağıdaki sınıfı göz önünde bulundurun:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

İş arkadaşlarım bunu şöyle tanımlamaya meyillidir:

class Person:
    name = None
    age = None

    def __init__(self, name, age):
        self.name = name
        self.age = age

Bunun temel nedeni, seçtikleri editörün otomatik tamamlama özelliklerini göstermesidir.

Şahsen, ikincisini beğenmedim, çünkü bir sınıfın bu özelliklere sahip olduğu bir anlam ifade etmiyor None.

Hangisi daha iyi uygulama olacaktır ve hangi sebeplerden dolayı?


63
IDE'nizin hangi kodu yazdığınızı belirtmesine asla izin vermeyin.
Martijn Pieters,

14
Bu arada: uygun bir python IDE (örn. PyCharm) __init__kullanarak , zaten otomatik tamamlama vb. Özellikleri ayarlama . Ayrıca, NoneIDE'nin özellik için daha iyi bir tip çıkarmasını önler, bu nedenle (yerine, makul bir varsayılan) kullanmak daha iyidir. mümkün).
Bakuriu,

Yalnızca otomatik tamamlama için kullanılıyorsa, tür ipucunu kullanabilirsiniz; ek belgeler de bir artı olacaktır.
19

3
"IDE'nizin hangi kodu yazdığınızı belirtmesine asla izin vermeyin" tartışmalı bir konudur. Python 3.6'dan itibaren, satır içi ek açıklamalar ve typingIDE ve linter ile ilgili ipuçları vermenize izin veren bir modül var, eğer bu tür bir şey düşkünlüğünüzü gıdıklıyorsa ...
cz

1
Sınıf düzeyinde yapılan bu ödevlerin kodun geri kalanına etkisi yoktur. Üzerinde etkisi yoktur self. Bile self.nameveya self.ageatanmış değildi __init__durumda görünmeyen onlar self, sadece sınıfta görünür Person.
jolvi

Yanıtlar:


70

"Bu, senin yaptığını düşündüğünü yapmaz" kuralı altındaki ikinci kötü uygulama olarak adlandırıyorum.

İş arkadaşınızın pozisyonu şu şekilde yeniden yazılabilir: "Hiçbir zaman erişilemeyen, ancak çeşitli sınıfın ad alan tablolarında ( __dict__) yer alan, IDE'nin yapması için yer kaplayan bir grup sınıf-statik yarı-global değişken yaratacağım. bir şey."


4
Öyleyse biraz docstrings ;-) Tabii ki, Python, -OOihtiyacı olanlar için , bunları soymak için kullanışlı bir moda sahiptir .
Steve Jessop,

15
Alınan alan, bu konvansiyonun kokuşmasının en az önemli nedenidir. İsim başına bir düzine bayt (işlem başına). Vaktimi boşa harcadığımı hissediyorum , cevabınızın sadece bu konuya ilişkin cevabını okudum , mekan maliyeti ne kadar önemli değil.

8
@delnan Girişlerin hafıza büyüklüğünün anlamsız olduğu konusunda hemfikirim, mantıksal / zihinsel boşluğa daha fazla yaklaşıyordum, içsel hata ayıklama yapıyordum ve daha fazla okuma ve sıralama gerektiriyordum, sanırım. Ben ~ LL
StarWeaver

3
Aslında, uzayla ilgili bu paranoyak olsaydın, bunun yerden tasarruf sağladığını ve zaman harcadığını fark edersiniz. Başlatılmamış üyeler sınıf değerini kullanmaya başlarlar ve bu nedenle Yok olarak eşlenen değişken adının bir kopyası yoktur (her bir örnek için bir tane). Bu yüzden maliyet sınıftaki birkaç bayttır ve tasarruf örnek başına birkaç bayttır. Ancak her başarısız değişken adı araması çok az zaman alır.
Jon Jay Obermark

2
8 byte hakkında endişeli olsaydın Python kullanmazdın.
cz

26

1. Kodunuzun anlaşılmasını kolaylaştırın

Kod yazılandan çok daha sık okunur. Kod sağlayıcınızın görevini kolaylaştırın (gelecek yıl kendiniz de olabilir).

Sert kurallar hakkında hiçbir fikrim yok, ancak gelecekteki herhangi bir durumun açıkça açıkça ilan edilmesini tercih ederim . Bir ile gürültüyle çarpmak AttributeErroryeterince kötü. Bir örnek niteliğinin yaşam döngüsünü açıkça görmemek, daha kötüdür. Atanan arama dizilerini geri yüklemek için gereken zihin jimnastiğinin miktarı, atanan özelliğe neden olan kolayca önemsiz hale gelebilir ve hatalara yol açabilir.

Bu yüzden genellikle sadece yapıcıdaki her şeyi tanımlamakla kalmaz, aynı zamanda değişken niteliklerin sayısını minimumda tutmaya çalışırım.

2. Sınıf ve örnek seviye üyelerini karıştırmayın.

classBildirimin içinde tanımladığınız her şey sınıfa aittir ve sınıfın tüm örnekleri tarafından paylaşılır. Örneğin, bir sınıf içindeki bir işlevi tanımladığınızda, tüm örnekler için aynı olan bir yöntem haline gelir. Aynısı veri üyeleri için de geçerlidir. Bu tamamen tanımladığınız örnek özelliklerin aksinedir __init__.

Sınıf düzeyinde veri üyeleri en çok sabit olarak kullanışlıdır:

class Missile(object):
  MAX_SPEED = 100  # all missiles accelerate up to this speed
  ACCELERATION = 5  # rate of acceleration per game frame

  def move(self):
    self.speed += self.ACCELERATION
    if self.speed > self.MAX_SPEED:
      self.speed = self.MAX_SPEED
    # ...

2
Evet, ancak sınıf ve örnek seviye üyelerini karıştırmak, def'in yaptığı şeydir . Nesnelerin öznitelikleri olarak düşündüğümüz, ancak gerçekten sınıfın üyeleri olan fonksiyonlar yaratır. Aynı şey mülk ve ilk için de geçerli. İşe yarayan yanılsamayı vermek, sınıf tarafından gerçekten aracılık edildiğinde nesnelere aittir ve fındık yememenin onurlu bir yoludur. Python'un kendisi için uygunsa, nasıl bu kadar kötü olabilir.
Jon Jay Obermark

Peki, Python'daki (veri üyelerine de uygulanabilir) yöntem çözümleme sırası çok basit değil. Örnek düzeyinde bulunmayan, sınıf düzeyinde, daha sonra temel sınıflar vb. Arasında aranacaktır. Gerçekten aynı adlandırılmış örnek düzeyinde bir üye atayarak sınıf düzeyinde bir üyeyi (veri veya yöntem) gölgeleyebilirsiniz. Ancak, örnek düzeyindeki yöntemler, eşgörünümlere bağlıdır ve geçilmesi selfgerekmez self, sınıf düzeyi yöntemler sınırsızdır ve görüldüğü gibi düz işlevlerdir defve bir örneği ilk argüman olarak kabul eder. Yani bunlar farklı şeyler.
9000,

Bence bir AttributeErrorböcek olmaktansa iyi bir sinyaldir. Aksi takdirde, Yok'u yutar ve anlamsız sonuçlar alırsınız. Elde edilen, niteliklerin tanımlandığı durumda özellikle önemli olan __init__, bu nedenle eksik bir nitelik (ancak sınıf düzeyinde var olan) yalnızca bu tür kalıtımdan kaynaklanabilir.
Davidmh

@Davidmh: Algılanan bir hata her zaman tespit edilemeyen bir hatadan iyidir, gerçekten! Bir öznitelik yapmak zorundaysanız Noneve bu değer , örnek yapım aşamasında geçerli olmazsa , mimarinizde bir sorun olduğunu ve özniteliğin değerinin ya da başlangıç ​​değerinin yaşam döngüsünü yeniden düşünmeniz gerektiğini söyleyebilirim . Özniteliklerinizi erken tanımlayarak, sınıfın geri kalanını yazmadan önce bile, kodu çalıştırmanın tek başına böyle bir sorunu tespit edebileceğinizi unutmayın.
9000

Eğlence! füzeler! Neyse, ben ... o sınıf düzeyindeki değişkenler yapmak için OK eminim ve onları karıştırmak sürece sınıf seviyesi vb varsayılan, içerdiği
Erik Aronesty

18

Şahsen ben üyeleri __ init __ () metodunda tanımlarım. Onları sınıf bölümünde tanımlamayı hiç düşünmedim. Ama her zaman yaptığım şey: __ init__ yöntemindeki tüm üyeleri, __ init__ yönteminde gerekmeyenleri bile init ediyorum.

Örnek:

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age
        self._selected = None

   def setSelected(self, value):
        self._selected = value

Tüm üyeleri tek bir yerde tanımlamanın önemli olduğunu düşünüyorum. Kodu daha okunaklı hale getirir. __ init __ () içinde mi yoksa dışında mı olduğu da o kadar önemli değil. Ancak bir ekibin aynı kodlama stiline az çok karar vermesi önemlidir.

Oh, fark edersiniz ki "_" ön ekini üye değişkenlerine ekledim.


13
Yapıcıdaki tüm değerleri ayarlamanız gerekir. Değer sınıf içinde ayarlandıysa, hiçbiri için iyi olan ancak çoğu değerler için iyi olmayan örnekler arasında paylaşılır. Yani bu konuda hiçbir şeyi değiştirmeyin. ;)
Remco Haszing

4
Parametreler için varsayılan değerler ve isteğe bağlı konumlandırma ve anahtar kelime argümanları alma yeteneği, beklenenden çok daha az acı verici hale getirir. (Ve aslında inşaatı başka yöntemlere veya hatta bağımsız işlevlere devretmek mümkündür, bu yüzden gerçekten çok sayıda gönderime ihtiyacınız varsa, bunu da yapabilirsiniz).
Sean Vieira

6
@Benedict Python erişim kontrolü yok. Önde gelen alt çizgi, uygulama detayları için kabul edilen sözleşmedir. PEP 8'e bakınız .
Doval,

3
@Doval Özellik adlarını öneklemek için olağanüstü iyi bir neden var _: Özel olduğunu belirtmek için! (Neden bu konudaki pek çok kişi Python'u diğer dillerle karıştırıyor veya

1
Ah, öyleyse siz herkese maruz kalan etkileşimli insanların özelliklerinden veya işlevlerinden birisiniz ... Yanlış anlaşıldığım için üzgünüm. Bu tarz bana her zaman aşırı geliyor, ama bir iz bırakıyor.
Jon Jay Obermark

11

Bu kötü bir uygulamadır. Bu değerlere ihtiyacınız yoktur, kodu bozarlar ve hatalara neden olabilirler.

Düşünmek:

>>> class WithNone:
...   x = None
...   y = None
...   def __init__(self, x, y):
...     self.x = x
...     self.y = y
... 
>>> class InitOnly:
...   def __init__(self, x, y):
...     self.x = x
...     self.y = y
... 
>>> wn = WithNone(1,2)
>>> wn.x
1
>>> WithNone.x #Note that it returns none, no error
>>> io = InitOnly(1,2)
>>> InitOnly.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class InitOnly has no attribute 'x'

Buna 'hata yapan' demeye zorlanırdım. Burada 'x' talebinde bulunmakla neyi kastettiğiniz kesin değil, ancak en muhtemel başlangıç ​​değerini çok iyi isteyebilirsiniz.
Jon Jay Obermark

Yanlış kullanılırsa bir hataya neden olabileceğini düşünmeliydim.
Daenyth,

Daha yüksek risk hala bir nesneyi başlatıyor. Hiçbiri gerçekten güvenli değil. Sebep olacağı tek hata, gerçekten topal beyinli istisnalar yutmaktır.
Jon Jay Obermark

1
Hatalara neden olmamak, daha ciddi sorunları önleyebilecek bir problemdir.
Erik Aronesty,

0

"Biraz dokümanlar gibi, sonra" ile gideceğim ve bu zararsız, her zaman olduğu gibiNone , ya da her zaman değişmeyen, diğer değerler arasında dar bir değer olduğunu ilan edeceğim.

Atavism ve statik olarak yazılan dillere aşırı bağlanma kokuyor. Ve kod olarak iyi değil. Ancak dokümantasyonda kalan küçük bir amacı vardır.

Beklenen adların ne olduğunu belgelemektedir, bu nedenle kodu biriyle kullanırsak ve birimizin 'kullanıcı adı' ve diğer kullanıcı_adı varsa, insanlara ayırdığımız ve aynı değişkenleri kullanmadığımıza dair bir ipucu vardır.

Bir ilke olarak tam başlatmaya zorlamak aynı şeyi daha Pythonic bir şekilde gerçekleştirir, ancak içinde gerçek bir kod varsa __init__, bu kullanılan değişkenleri belgelemek için daha net bir yer sağlar.

Açıkçası, buradaki BÜYÜK sorun, insanları Nonekötü olabilecek dışındaki değerlerle başlatmaya teşvik ediyor :

class X:
    v = {}
x = X()
x.v[1] = 2

küresel bir iz bırakır ve x için bir örnek oluşturmaz.

Fakat bu, Python'da bir bütün olarak bu uygulamadan çok bir tuhaflık ve bu konuda zaten paranoyak olmalıyız.

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.