Python'da açıkça 'kendiliğinden' nasıl kaçınılır?


131

Bazılarını takip ederek Python öğreniyorum pygame eğitimlerini ederek .

Orada Anahtar kelime geniş kullanım alanı bulmuş kendini ve öncelikle Java arka plandan gelen, ben yazmaya unutuyorum bulmak kendini . Örneğin, yerine self.rect.centerxben yazarsınız rect.centerx, bana göre, çünkü rect zaten sınıfın üyesi değişkendir.

Java paralel bu durum ile üye değişkenleri ile ilgili tüm içeriği önek yaşıyor için aklınıza gelebilecek bu .

Tüm üye değişkenlerin önüne self koyuyor muyum, yoksa bunu yapmak zorunda kalmamamı sağlayacak onları bildirmenin bir yolu var mı?

Önerdiğim şey olmasa bile Önerdiğim pitonik olmasa bile, yine de mümkün olup olmadığını bilmek isterim.

Bu ilgili SO sorularına bir göz attım, ancak peşinde olduğum şeyi tam olarak yanıtlamıyorlar:


5
Java geçmişinden geliyorum ve bunu doğal buluyorum, ancak bir örnek değişkenine atıfta bulunduğumu daha net hale getirmek için her çağrıya açıkça "bu" kelimesini ekliyorum.
Uri

4
m_Bazı C ++ / Java programcıları tarafından gözlemlenen tüm üye adları için bir önek kuralına aşina mısınız ? Kullanımı, self.okunabilirliğe benzer şekilde yardımcı olur. Ayrıca dirtsimple.org/2004/12/python-is-not-java.html'yi de okumalısınız .
Beni Cherniavsky-Paskin

2
Genellikle m_, yalnızca genel olmayan statik olmayan veri üyeleri için kullanılır (en azından C ++ 'da).

@Beni harika bağlantılı makale ve evet, aslında mVariableNameJava'da kodlama yaparken üye değişkenler için kullanma kuralını takip ediyorum . Sanırım @ Anurag'ın yorumu, bir java geliştiricinin python öğrenirken ne yapması gerektiği konusunda bunu oldukça iyi özetliyor.
bguiz

11
Öyleyse neden herkes OP'ye kendini kullanmanın neden iyi / gerekli / vb. Olduğunu söylüyor? ama kimse bir şekilde önlenip önlenemeyeceğini söylemiyor mu? Bir tür kirli numara olsa bile mi?
einpoklum

Yanıtlar:


99

Python, kendini belirtmeyi gerektirir. Sonuç, sınıf tanımının tamamı görünmese bile, neyin üye olup neyin olmadığı konusunda hiçbir zaman kafa karışıklığı olmamasıdır. Bu, aşağıdaki gibi yararlı özelliklere yol açar: üye olmayanları yanlışlıkla gölgeleyen ve dolayısıyla kodu bozan üyeler ekleyemezsiniz.

Uç bir örnek: Bir sınıfı hangi temel sınıflara sahip olabileceğine dair hiçbir bilgi olmadan yazabilir ve bir üyeye erişip erişmediğinizi her zaman bilirsiniz:

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()

Budur komple kod! (bazı_işlev, temel olarak kullanılan türü döndürür.)

Bir sınıfın yöntemlerinin dinamik olarak oluşturulduğu bir diğeri:

class B(object):
  pass

print B()
# <__main__.B object at 0xb7e4082c>

def B_init(self):
  self.answer = 42
def B_str(self):
  return "<The answer is %s.>" % self.answer
# notice these functions require no knowledge of the actual class
# how hard are they to read and realize that "members" are used?

B.__init__ = B_init
B.__str__ = B_str

print B()
# <The answer is 42.>

Unutmayın, bu örneklerin her ikisi de aşırıdır ve onları her gün görmezsiniz, ne de sık sık böyle kod yazmanızı önermiyorum, ancak açıkça kendiliğin açıkça gerekli olan yönlerini gösterirler.


4
Teşekkür ederim. Bu cevap tam olarak isabet ediyor çünkü aynı zamanda kullanmanın faydalarını da açıklıyor self.
bguiz

2
@Roger Pate: Python'u ondan kaldırmak için lütfen sorumu düzenlemeyi bırakın. Oraya ait olduğunu düşünüyorum. (ve cevabınız için teşekkürler!)
bguiz

3
@bguiz: Başlıktaki etiketleri çoğaltmamak SO kuralı. Ancak, 2 gün önce düzenleme yaptığımda, başlığı 7 ay önce geri aldığınızı görmedim.

1
Benlik tek bir karaktere indirgenebilseydi güzel olurdu.
dwjohnston

5
Bu aslında kulağa oldukça aptalca bir neden. Python, gölgelemeyi kabul etmekte başarısız olabilir ve sizden bunu özellikle bildirmenizi isteyebilir, yani gölgeleme alanınızı bir tür ile 'kutsayın' __shadow__ myFieldName. Bu yanlışlıkla gölgelenmeyi de önler, değil mi?
einpoklum

39

Önceki yanıtların tümü temelde "yapamazsın" veya "yapmamalısın" ifadelerinin varyantlarıdır. İkinci düşünceye katılıyorum, ancak soru teknik olarak hala cevaplanmamış.

Dahası, birisinin asıl sorunun sorduğu çizgide bir şeyler yapmak istemesinin meşru nedenleri vardır. Bazen karşılaştığım bir şey, uzun isimler kullanmanın denklemi tanınmaz hale getirdiği uzun matematik denklemleridir. Hazır bir örnekte bunu nasıl yapabileceğinizin birkaç yolu:

import numpy as np
class MyFunkyGaussian() :
    def __init__(self, A, x0, w, s, y0) :
        self.A = float(A)
        self.x0 = x0
        self.w = w
        self.y0 = y0
        self.s = s

    # The correct way, but subjectively less readable to some (like me) 
    def calc1(self, x) :
        return (self.A/(self.w*np.sqrt(np.pi))/(1+self.s*self.w**2/2)
                * np.exp( -(x-self.x0)**2/self.w**2)
                * (1+self.s*(x-self.x0)**2) + self.y0 )

    # The correct way if you really don't want to use 'self' in the calculations
    def calc2(self, x) :
        # Explicity copy variables
        A, x0, w, y0, s = self.A, self.x0, self.w, self.y0, self.s
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

    # Probably a bad idea...
    def calc3(self, x) :
        # Automatically copy every class vairable
        for k in self.__dict__ : exec(k+'= self.'+k)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

g = MyFunkyGaussian(2.0, 1.5, 3.0, 5.0, 0.0)
print(g.calc1(0.5))
print(g.calc2(0.5))
print(g.calc3(0.5))

Üçüncü örnek - yani kullanmak for k in self.__dict__ : exec(k+'= self.'+k), temelde sorunun aslında istediği şeydir, ancak bunun genel olarak iyi bir fikir olduğunu düşünmediğimi açıklığa kavuşturayım.

Daha fazla bilgi ve sınıf değişkenleri ve hatta işlevler arasında yineleme yapmanın yolları için, bu sorunun yanıtlarına ve tartışmaya bakın . Değişkenleri dinamik olarak adlandırmanın diğer yolları ve bunun neden genellikle iyi bir fikir olmadığı hakkında bir tartışma için bu blog gönderisine bakın .

GÜNCELLEME: olduğu görülmektedir hiçbir şekilde calc3 ve benzeri varyantlar artık mümkün böylece, dinamik güncelleştirme veya Python3 bir işlev değişikliği halk için. Şu an düşünebildiğim python3 uyumlu tek çözüm şudur globals:

def calc4(self, x) :
        # Automatically copy every class variable in globals
        globals().update(self.__dict__)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

Bu da yine genel olarak korkunç bir uygulama olurdu.


4
Parlak! Bu en (tek) doğru cevaptır. +1 Ayrıca bunu yapmak için benzersiz bir şekilde pratik bir neden veriyorsunuz. + 1i
David Lotts

Bir sınıf oluşturduktan ve içindeki kodu taşıdıktan sonra: artık tüm yöntemler ve değişkenler artık tanınmıyor ( benlik yok ...) Bir başka neden python hatırlatıldı ve anlaşamıyorum. Bunun için bir fikir olarak teşekkürler. Genel olarak sorunu / baş ağrısını / okunamazlığı çözmez, ancak mütevazı bir geçici çözüm sağlar.
javadba

Neden localskullanmak yerine sadece güncellemiyorsunuz exec?
Nathan

locals().update(self.__dict__)2. ve 3. python'da denedim ama işe yaramadı. Python3'te 'exec' numarası bile artık bir seçenek değil. Öte yandan, globals().update(self.__dict__)işe yarıyor, ancak genel olarak korkunç bir uygulama olur.
argentum2f

26

Aslında selfbir anahtar kelime değildir, sadece Python'daki örnek yöntemlerinin ilk parametresine geleneksel olarak verilen addır. Ve bu ilk parametre, bir yöntemin sınıfınızın hangi örneğinin çağrıldığını bilmek için sahip olduğu tek mekanizma olduğu için atlanamaz.


1
Bu cevap, özellikle 2. cümle benim için kabul edilen cevaptan çok daha kullanışlıdır, çünkü 'açık benliğin' Python'daki sınırlamalardan sadece biri olduğunu ve önlenemez olduğunu bilebilirim
QuestionDriven

21

Örneğin, istediğiniz adı kullanabilirsiniz.

class test(object):
    def function(this, variable):
        this.variable = variable

ya da

class test(object):
    def function(s, variable):
        s.variable = variable

ancak kapsam için bir ad kullanmak zorunda kalıyorsunuz.

Deneyimli pythonistalar için onu yabancı yapacağından, ikna edici bir nedeniniz olmadıkça kendinizden farklı bir şey kullanmanızı önermiyorum.


26
Bunu yapabilirsin ama yapma ! Kodunuzu olması gerekenden daha tuhaf yapmak için hiçbir neden yok. Evet, buna herhangi bir şey diyebilirsiniz, ancak kongre onu adlandırmaktır selfve kongreye uymalısınız. Kodunuza bakan herhangi bir deneyimli Python programcısı için kodunuzun anlaşılmasını kolaylaştıracaktır. (Bu, altı ay sonra, eski programınızın ne yaptığını anlamaya çalışmanızı da içerir!)
steveha

3
Daha da uzaylı:def function(_, variable): _.variable = variable
Bob Stein

1
@ BobStein-VisiBone daha da uzaylı:def funcion(*args): args[0].variable = args[1]
Aemyl

4
@steveha tavsiye etmiyor, bu bilgi benim gibi kendinizden farklı anahtar sözcükler kullanabileceğinizi bilmeyen ve kendi sınıfından bir nesnenin neden geçtiğini merak eden kişiler için çok faydalıdır.
Sven van den Boogaart

> "daha tuhaf". Python olduğunu özellikle - garip sınıflar yapıları ve bu kullanımı öz karşıtı okunabilirliği desteklemek için bir kanarya olduğunu. Bundan sonra birçok savunmacının geleceğini biliyorum ama bu gerçeği değiştirmiyor.
javadba

9

evet, her zaman belirtmelisiniz self, çünkü python felsefesine göre açık, örtükten daha iyidir.

Ayrıca, python'da programlama şeklinizin java'daki programlamadan çok farklı olduğunu da öğreneceksiniz, bu nedenle selfnesnenin içindeki her şeyi yansıtmadığınız için kullanımı azalmaya meyillidir. Bunun yerine, daha iyi test edilebilecek modül düzeyinde işlevi daha geniş bir şekilde kullanırsınız.

bu arada. İlk başta nefret ettim, şimdi tam tersinden nefret ediyorum. girintili tahrikli akış kontrolü için aynı.


2
"Daha iyi test edilebilecek modül düzeyinde işlevi daha geniş bir şekilde kullanıyorsunuz" şüpheli ve kesinlikle katılmıyorum. "Mantıksal olarak modül düzeyinde" olup olmadığına bakılmaksızın, her şeyi bir sınıfın yöntemi (statik veya değil) yapmaya zorlamadığınız doğrudur, ancak bunun kendinizle hiçbir ilgisi yoktur ve üzerinde önemli bir etkisi yoktur. bir yolu veya diğerini test etmek (benim için).

Benim deneyimimden kaynaklanıyor. Bunu her seferinde bir mantra olarak takip etmiyorum, ancak üye değişken erişimini gerektirmeyen herhangi bir şeyi ayrı, bağımsız bir yöntem olarak koyarsanız, işleri test etmeyi kolaylaştırır. evet, mantığı veriden ayırırsınız, bu evet, OOP'ye aykırıdır, ancak bunlar birliktedir, sadece modül seviyesinde. Birine veya diğerine "en iyi" işareti vermiyorum, bu sadece bir zevk meselesi. Bazen kendimi sınıfın kendisiyle hiçbir ilgisi olmayan sınıf yöntemlerini belirtirken buluyorum, çünkü bunlar selfhiçbir şekilde temas etmiyor . Peki onları sınıfta bulundurmanın ne anlamı var?
Stefano Borini

Her iki kısmına da katılmıyorum ("yöntem olmayanları" diğer dillerden daha sık kullanmıyorum gibi görünüyor), ancak "daha iyi test edilebilen" bir yolun diğerinden daha üstün olduğunu ima ediyor (tam da bu şekilde Okudum mu? olası görünmüyor), ancak deneyimlerime göre bunun için destek bulamıyorum. Her zaman birini veya diğerini kullanmanız gerektiğini söylemediğime dikkat edin, sadece yöntemlerin ve yöntem olmayanların eşit derecede test edilebileceğini söylüyorum.

5
evet, elbette yapabilirsin, ama yaklaşım farklı. Bir nesnenin bir durumu vardır, bir modül seviyesi yöntemi yoktur. Sınıf düzeyinde bir yöntemi test ederken bir testin başarısız olduğunu fark ederseniz, iki şey yanlış olabilir: 1) arama sırasında nesnenin durumu 2) yöntemin kendisi. Durum bilgisi olmayan modül düzeyinde bir yönteminiz varsa, yalnızca durum 2 gerçekleşebilir. Kurulumu nesneden (test söz konusu olduğunda kara kutu olduğu, çünkü nesnenin içindeki karmaşık mantık tarafından yönetildiği için) test takımına taşıdınız. Karmaşıklığı azaltıyorsunuz ve kurulumu daha sıkı kontrol ediyorsunuz.
Stefano Borini

1
"Durum bilgisi olmayan modül düzeyinde bir yönteminiz varsa", durum bilgisi olan modül düzeyinde bir yönteme ne dersiniz? Bana söylediğin tek şey, durum bilgisi olmayan fonksiyonların test edilmesinin durum bilgisi olanlardan daha kolay olduğu ve ben buna katılıyorum, ama bunun metot olmayanlara karşı metot olmayanlarla ilgisi yok. Self parametresini tam olarak şu şekilde görüntüleyin: işleve yalnızca başka bir parametre.

4

"Öz", bir sınıfın mevcut nesne örneğinin geleneksel yer tutucusudur. Bir sınıf içindeki nesnenin özelliğine veya alanına veya yöntemine "kendisine" atıfta bulunuyormuşsunuz gibi başvurmak istediğinizde kullanılır. Fakat onu kısaltmak için Python programlama dünyasında biri "self" i kullanmaya başladı, diğer alemler "this" i kullanır ama onu değiştirilemeyen bir anahtar kelime olarak yaparlar. Kod okunabilirliğini artırmak için "its" i kullandım. Python'daki en iyi şeylerden biri - nesnenin örneği için "self" dışında kendi yer tutucunuzu seçme özgürlüğüne sahipsiniz. Kendine örnek:

class UserAccount():    
    def __init__(self, user_type, username, password):
        self.user_type = user_type
        self.username = username            
        self.password = encrypt(password)        

    def get_password(self):
        return decrypt(self.password)

    def set_password(self, password):
        self.password = encrypt(password)

Şimdi 'ben'i' onun 'ile değiştiriyoruz:

class UserAccount():    
    def __init__(its, user_type, username, password):
        its.user_type = user_type
        its.username = username            
        its.password = encrypt(password)        

    def get_password(its):
        return decrypt(its.password)

    def set_password(its, password):
        its.password = encrypt(password)

hangisi şimdi daha okunabilir?


neden sadece s(veya başka bir harf) yerineits
javadba

'onun' bir anlama sahiptir ve 's' yoktur.
LEMUEL ADANE

saynı anlama sahiptir: sınıf örneğinin takma adı. itsaynı bağlamda ne anlama geldiğine bakmak zorunda
kaldım

Her ikisi de bana okunabilir değil. "Kendinizi" harici bir parametre olarak görmek hala mantıksız, hiçbir anlam ifade etmiyor.
Cesar

3

self, nesnelerin üyelerine erişmek için kullanılan python sözdiziminin bir parçasıdır, bu yüzden korkarım ona takılıp kaldınız


2
self, erişim değiştiriciyi gerçekten kullanmadan söylemenin bir yoludur. +1
Perpetualcoder

1

Aslında Armin Ronacher "5 yıllık kötü fikirler" (google it) sunumundaki "Örtük benlik" tarifini kullanabilirsiniz.

Neredeyse Armin Ronacher'deki her şey gibi çok akıllıca bir tarif, ama bu fikrin çok çekici olduğunu düşünmüyorum. Ben açık tercih ediyorum düşünüyorum bu C # / Java.

Güncelleme. "Kötü fikir tarifi" bağlantısı: https://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58


bu size atıfta ne kastediyoruz bağlantı? Öyleyse, lütfen cevabınıza ekleyin
reubenjohn

Hayır, Armin "kötü fikir" benim zevkime göre daha komik görünüyor. Bağlantı ekledim.
Alex Yu

Tarif bağlantısını tıklamadan önce, bunun def method(<del> self </del> )parametre listesinde örtük olduğunu , ancak self.variableyine de bu akıllı hack için gerekli olduğunu unutmayın.
David Lotts

0

Evet, benlik sıkıcıdır. Ama daha mı iyi?

class Test:

    def __init__(_):
        _.test = 'test'

    def run(_):
        print _.test

10
_Son dönen değeri tuttuğu Python kabuğunda özel bir anlama sahiptir. Bu şekilde kullanmak güvenlidir, ancak potansiyel olarak kafa karıştırıcıdır; Bundan kaçınırdım.
Cairnarvon

Hayır, bu daha iyi değil ama neden bunun yerine tek bir harf olmasın sveya m(C ++ 'yı taklit etmek için)
javadba

0

From: Self Hell - Daha durum bilgisi olan işlevler.

... melez bir yaklaşım en iyi sonucu verir. Gerçekte hesaplama yapan tüm sınıf yöntemleriniz kapanışlara taşınmalı ve sözdizimini temizlemek için uzantılar sınıflarda tutulmalıdır. Kapakları sınıflara doldurun, sınıfa daha çok bir ad alanı gibi davranın. Kapanışlar aslında statik fonksiyonlardır ve bu yüzden sınıfta bile selfs * gerektirmezler ...


Kapatma işlemleri küçük kullanım durumları için iyidir, ancak bunların uzun süreli kullanımı bir programın bellek yükünü oldukça artıracaktır (sınıf tabanlı OO yerine prototip tabanlı OO kullandığınız için - bu nedenle her nesne kendi işlev kümesini gerektirir daha ziyade bir sınıfta ortak bir işlev kümesi tutmak). Ayrıca, bunlar __str__normal yöntemlerden farklı bir şekilde çağrıldıklarından, yöntemleri (örn. Ve benzeri) büyü / dunder yapmanıza engel olacaktır .
Kumullar

0

Bence "üye" ifadesi de olsaydı, tıpkı "global" olduğu gibi, yorumlayıcıya hangi nesnelerin sınıfın üyeleri olduğunu söyleyebilirsiniz.

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.