Bagaimana cara menghindari 'diri' secara eksplisit dengan Python?

131

Saya telah belajar Python dengan mengikuti beberapa tutorial pygame .

Di sana saya menemukan banyak penggunaan kata kunci diri , dan terutama berasal dari latar belakang Jawa, saya menemukan bahwa saya terus lupa mengetik sendiri . Sebagai contoh, bukannya self.rect.centerxsaya akan mengetik rect.centerx, karena, bagi saya, rect sudah menjadi variabel anggota kelas.

Paralel Java yang dapat saya pikirkan untuk situasi ini adalah harus mengawali semua referensi ke variabel anggota dengan ini .

Apakah saya terjebak dengan awalan semua variabel anggota dengan diri saya sendiri , atau adakah cara untuk mendeklarasikannya agar saya dapat menghindari keharusan melakukannya?

Bahkan jika apa yang saya sarankan bukanlah pythonic , saya masih ingin tahu apakah itu mungkin.

Saya telah melihat pada pertanyaan-pertanyaan SO terkait ini, tetapi mereka tidak cukup menjawab apa yang saya cari:

bguiz
sumber
5
Saya berasal dari latar belakang Java dan menemukannya alami, tetapi saya secara eksplisit menambahkan "ini" ke setiap panggilan untuk membuatnya lebih jelas bahwa saya merujuk ke variabel instan.
Uri
4
Apakah Anda terbiasa dengan konvensi m_awalan untuk semua nama anggota yang diamati oleh beberapa programmer C ++ / Java? Penggunaan self.membantu keterbacaan dengan cara yang sama. Anda juga harus membaca dirtsimple.org/2004/12/python-is-not-java.html .
Beni Cherniavsky-Paskin
2
Padahal biasanya m_digunakan hanya untuk anggota data non-publik non-statis (setidaknya dalam C ++).
@Beni artikel tertaut yang bagus, dan ya memang, saya benar-benar mengikuti konvensi menggunakan mVariableName, untuk variabel anggota, saat pengkodean di Jawa. Saya pikir komentar @ Anurag merangkumnya dengan cukup baik, untuk apa yang harus dilakukan oleh seorang pengembang java ketika belajar python.
bguiz
11
Jadi, bagaimana mungkin semua orang memberi tahu OP mengapa menggunakan diri itu baik / perlu / dll. tetapi tidak ada yang mengatakan apakah itu bisa dihindari atau tidak? Bahkan jika dengan semacam trik kotor?
einpoklum

Jawaban:

99

Python membutuhkan menentukan sendiri. Hasilnya adalah tidak pernah ada kebingungan tentang apa yang anggota dan apa yang tidak, bahkan tanpa definisi kelas penuh yang terlihat. Ini mengarah ke properti yang berguna, seperti: Anda tidak dapat menambahkan anggota yang secara tidak sengaja membayangi bukan anggota dan karenanya memecahkan kode.

Salah satu contoh ekstrem: Anda dapat menulis kelas tanpa pengetahuan tentang kelas dasar apa yang mungkin dimilikinya, dan selalu tahu apakah Anda mengakses anggota atau tidak:

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

Itu kode lengkap ! (some_function mengembalikan tipe yang digunakan sebagai basis.)

Lain, di mana metode kelas secara dinamis disusun:

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

Ingat, kedua contoh ini ekstrem dan Anda tidak akan melihatnya setiap hari, saya juga tidak menyarankan Anda harus sering menulis kode seperti ini, tetapi mereka jelas menunjukkan aspek diri yang secara eksplisit diperlukan.


sumber
4
Terima kasih. Jawaban ini mengenai tempat karena itu juga menjelaskan manfaat menggunakan self.
bguiz
2
@Roger Pate: Tolong berhenti mengedit pertanyaan saya untuk menghapus python dari itu. Saya pikir itu milik di sana. (dan terima kasih atas jawabannya!)
bguiz
3
@ bguiz: Ini adalah konvensi SO untuk tidak menduplikasi tag dalam judul. Namun, ketika saya mengedit 2 hari yang lalu, saya tidak melihat Anda mengembalikan judul 7 bulan yang lalu.
1
Alangkah baiknya jika diri bisa direduksi menjadi satu karakter.
dwjohnston
5
Itu sebenarnya alasan yang agak konyol. Python bisa gagal menerima bayangan, dan mengharuskan Anda untuk mendeklarasikannya secara spesifik, yaitu 'memberkati' bidang bayangan Anda dengan semacam __shadow__ myFieldName. Itu akan mencegah bayangan yang tidak disengaja juga, bukan?
einpoklum
39

Jawaban sebelumnya pada dasarnya adalah varian "Anda tidak bisa" atau "Anda tidak boleh". Sementara saya setuju dengan sentimen terakhir, pertanyaannya secara teknis masih belum terjawab.

Selain itu, ada alasan sah mengapa seseorang mungkin ingin melakukan sesuatu sesuai dengan pertanyaan yang diajukan. Satu hal yang sering saya temui adalah persamaan matematika yang panjang di mana menggunakan nama panjang membuat persamaan tidak dapat dikenali. Berikut adalah beberapa cara bagaimana Anda bisa melakukan ini dalam contoh kalengan:

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

Contoh ketiga - yaitu menggunakan for k in self.__dict__ : exec(k+'= self.'+k) pada dasarnya adalah pertanyaan yang sebenarnya ditanyakan, tetapi izinkan saya menjadi jelas bahwa menurut saya umumnya itu bukan ide yang baik.

Untuk info lebih lanjut, dan cara untuk mengulangi melalui variabel kelas, atau bahkan fungsi, lihat jawaban dan diskusi untuk pertanyaan ini . Untuk diskusi tentang cara lain untuk memberi nama variabel secara dinamis, dan mengapa ini biasanya bukan ide yang baik, lihat posting blog ini .

UPDATE: Tampaknya tidak ada cara untuk secara dinamis memperbarui atau mengubah lokal dalam suatu fungsi di Python3, sehingga calc3 dan varian serupa tidak lagi mungkin. Satu-satunya solusi yang kompatibel dengan python3 yang dapat saya pikirkan sekarang adalah menggunakan 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 )

Yang, sekali lagi, akan menjadi praktik yang mengerikan pada umumnya.

argentum2f
sumber
4
Cemerlang! Ini adalah jawaban yang paling benar (hanya?). +1 Anda juga secara unik memberikan alasan praktis untuk melakukannya. +1 1
David Lotts
Setelah membuat kelas dan memindahkan kode di dalamnya: sekarang semua metode dan variabel tidak lagi dikenali (tanpa diri ...) Saya diingatkan tentang alasan lain python dan saya tidak akur. Terima kasih untuk ini sebagai ide. Itu tidak memperbaiki masalah / sakit kepala / unreadability secara keseluruhan tetapi memberikan solusi sederhana.
javadba
Mengapa tidak memperbarui saja localsdaripada menggunakan exec?
Nathan
Saya mencoba locals().update(self.__dict__)dengan python 2 dan 3, tetapi tidak berhasil. Dalam python3 bahkan trik 'exec' bukan pilihan lagi. Di sisi lain, globals().update(self.__dict__)memang berhasil, tetapi akan menjadi praktik yang mengerikan pada umumnya.
argentum2f
26

Sebenarnya selfbukan kata kunci, itu hanya nama yang secara konvensional diberikan kepada parameter pertama metode contoh dengan Python. Dan parameter pertama itu tidak bisa dilewati, karena itu satu-satunya mekanisme yang dimiliki metode untuk mengetahui instance kelas Anda yang dipanggil.

Michał Marczyk
sumber
1
Jawaban ini, terutama kalimat ke-2, jauh lebih berguna daripada jawaban yang diterima bagi saya karena saya bisa tahu 'diri eksplisit' hanyalah salah satu batasan dalam Python dan tidak dapat dihindari
QuestionDriven
21

Anda dapat menggunakan nama apa pun yang Anda inginkan, misalnya

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

atau bahkan

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

tetapi Anda tidak dapat menggunakan nama untuk ruang lingkup.

Saya tidak menyarankan Anda menggunakan sesuatu yang berbeda untuk diri sendiri kecuali Anda memiliki alasan yang meyakinkan, karena itu akan membuatnya asing untuk pythonista yang berpengalaman.

Esteban Küber
sumber
26
Anda bisa melakukan ini, tetapi jangan ! Tidak ada alasan untuk membuat kode Anda lebih aneh dari yang seharusnya. Ya, Anda bisa menyebutnya apa saja, tetapi konvensi itu untuk menyebutnya self, dan Anda harus mengikuti konvensi itu. Ini akan membuat kode Anda lebih mudah dipahami oleh programmer Python berpengalaman yang melihatnya. (Ini termasuk Anda, enam bulan dari sekarang, mencoba mencari tahu apa program lama Anda!)
steveha
3
Yang lebih asing lagi:def function(_, variable): _.variable = variable
Bob Stein
1
@ BobStein-VisiBone bahkan lebih alien:def funcion(*args): args[0].variable = args[1]
Aemyl
4
@steveha dia tidak merekomendasikannya, informasi ini sangat membantu bagi orang-orang seperti saya yang tidak tahu Anda dapat menggunakan kata kunci yang berbeda dari diri sendiri dan bertanya-tanya mengapa objek kelas sendiri dilewatkan.
Sven van den Boogaart
> "lebih aneh". Python adalah aneh - terutama yang kelas struktur dan penggunaan ini sendiri adalah kenari untuk mendukung anti mudah dibaca. Saya tahu akan mendapatkan banyak pemain bertahan setelah ini tetapi itu tidak mengubah kebenaran.
javadba
9

ya, Anda harus selalu menentukan self , karena eksplisit lebih baik daripada implisit, menurut filosofi python.

Anda juga akan menemukan bahwa cara Anda memprogram dalam python sangat berbeda dari cara Anda memprogram di java, maka penggunaan selfcenderung menurun karena Anda tidak memproyeksikan segala sesuatu di dalam objek. Sebaliknya, Anda menggunakan fungsi tingkat modul yang lebih besar, yang dapat diuji lebih baik.

ngomong-ngomong. Awalnya aku benci, sekarang aku benci yang sebaliknya. sama untuk kontrol aliran indent-driven.

Stefano Borini
sumber
2
"Anda menggunakan fungsi tingkat modul yang lebih besar, yang bisa diuji lebih baik" meragukan, dan saya harus sangat tidak setuju. Memang benar Anda tidak dipaksa untuk membuat semuanya metode (statis atau tidak) dari beberapa kelas, terlepas dari apakah itu "secara logis modul-level", tetapi itu tidak ada hubungannya dengan diri sendiri dan tidak memiliki dampak signifikan pada menguji satu atau lain cara (untuk saya).
Itu muncul dari pengalaman saya. Saya tidak mengikutinya sebagai mantra setiap kali, tetapi hal itu membuat lebih mudah untuk menguji jika Anda meletakkan sesuatu yang tidak memerlukan akses variabel anggota sebagai metode terpisah dan independen. ya, Anda memisahkan logika dari data, yang ya, bertentangan dengan OOP, tetapi mereka bersama-sama, hanya pada tingkat modul. Saya tidak memberikan satu atau yang lain tanda "terbaik", itu hanya masalah selera. Kadang-kadang saya menemukan diri saya menentukan metode kelas yang tidak ada hubungannya dengan kelas itu sendiri, karena mereka tidak menyentuh selfdengan cara apa pun. Jadi apa gunanya memiliki mereka di kelas?
Stefano Borini
Saya tidak setuju dengan kedua bagian itu (sepertinya saya menggunakan "non-metode" tidak lebih sering daripada dalam bahasa lain), tetapi "yang dapat diuji lebih baik" tidak menyiratkan bahwa satu cara lebih unggul dari yang lain (apakah itu cara saya membacanya? sepertinya tidak mungkin), sementara saya tidak menemukan dukungan untuk itu dalam pengalaman saya. Perhatikan bahwa saya tidak mengatakan Anda harus selalu menggunakan satu atau yang lain, saya hanya mengatakan metode dan non-metode sama-sama dapat diuji.
5
ya, tentu saja Anda bisa, tetapi pendekatannya berbeda. Objek memiliki status, metode level modul tidak. Jika Anda menemukan tes gagal saat menguji metode tingkat kelas, dua hal bisa saja salah: 1) keadaan objek pada saat panggilan 2) metode itu sendiri. Jika Anda memiliki metode level modul stateless, hanya case 2 yang bisa terjadi. Anda memindahkan pengaturan dari objek (di mana itu adalah kotak hitam karena tes yang bersangkutan, karena itu diatur oleh logika kompleks akhirnya di dalam objek) ke testuite. Anda mengurangi kompleksitas dan menjaga kontrol pengaturan yang lebih ketat.
Stefano Borini
1
"Jika Anda memiliki metode tingkat modul stateless", bagaimana dengan metode tingkat modul stateful? Yang Anda katakan kepada saya adalah bahwa fungsi stateless lebih mudah untuk diuji daripada yang stateful, dan saya akan setuju dengan itu, tetapi itu tidak ada hubungannya dengan metode vs non-metode. Lihat parameter mandiri seperti itu: hanya parameter lain ke fungsi.
4

"Diri" adalah placeholder konvensional dari instance objek saat ini dari kelas. Ini digunakan ketika Anda ingin merujuk ke properti objek atau bidang atau metode di dalam kelas seolah-olah Anda merujuk ke "sendiri". Tetapi untuk membuatnya lebih pendek seseorang di ranah pemrograman Python mulai menggunakan "diri", ranah lain menggunakan "ini" tetapi mereka menjadikannya sebagai kata kunci yang tidak dapat diganti. Saya lebih suka menggunakan "nya" untuk meningkatkan keterbacaan kode. Ini salah satu hal baik dalam Python - Anda memiliki kebebasan untuk memilih placeholder Anda sendiri untuk contoh objek selain "diri". Contoh untuk diri sendiri:

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)

Sekarang kita ganti 'diri' dengan 'nya':

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)

mana yang lebih mudah dibaca sekarang?

LEMUEL ADANE
sumber
mengapa tidak hanya s(atau satu huruf tunggal) alih-alihits
javadba
'its' memiliki arti dan 's' tidak.
LEMUEL ADANE
smemiliki arti yang sama: alias ke instance kelas. saya harus mencari apa itsartinya dalam konteks yang sama
javadba
Keduanya tidak dapat dibaca oleh saya. Masih tidak masuk akal untuk mendapatkan "diri sendiri" sebagai parameter eksternal, itu tidak masuk akal.
Cesar
3

diri adalah bagian dari sintaks python untuk mengakses anggota objek, jadi saya khawatir Anda terjebak dengannya

Charles Ma
sumber
2
diri adalah cara untuk memberitahu pengubah akses tanpa benar-benar menggunakannya. +1
Perpetualcoder
1

Sebenarnya Anda bisa menggunakan resep "Implisit diri" dari presentasi Armin Ronacher "5 tahun ide buruk" (google it).

Ini resep yang sangat pintar, karena hampir semuanya dari Armin Ronacher, tetapi saya tidak berpikir ide ini sangat menarik. Saya pikir saya lebih suka eksplisit ini di C # / Java.

Memperbarui. Tautan ke "resep ide buruk": https://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58

Alex Yu
sumber
Apakah ini tautan yang Anda maksudkan dengan apa yang Anda maksud? Jika demikian, mohon sertakan dalam jawaban Anda
reubenjohn
Tidak, Armin "ide buruk" terlihat lebih lucu untuk seleraku. Saya menyertakan tautan.
Alex Yu
Sebelum Anda mengklik tautan resep, perhatikan bahwa ini membuatnya tersirat dalam daftar parameter def method(<del> self </del> ), tetapi self.variablemasih diperlukan dengan peretas cerdas ini.
David Lotts
0

Ya, diri itu membosankan. Tapi, apakah ini lebih baik?

class Test:

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

    def run(_):
        print _.test

sumber
10
_memiliki arti khusus di shell Python, di mana ia memegang nilai yang dikembalikan terakhir. Aman untuk menggunakannya seperti ini, tetapi berpotensi membingungkan; Saya akan menghindarinya.
Cairnarvon
Tidak, itu tidak lebih baik tetapi mengapa tidak satu huruf saja, misalnya, satau m(untuk meniru C ++)
javadba
0

From: Self Hell - Lebih banyak fungsi stateful.

... pendekatan hibrida paling berhasil. Semua metode kelas Anda yang benar-benar melakukan komputasi harus dipindahkan ke penutupan, dan ekstensi untuk membersihkan sintaksis harus disimpan di kelas. Masukkan penutup ke dalam kelas, perlakukan kelas seperti namespace. Penutupan pada dasarnya adalah fungsi statis, dan karenanya tidak memerlukan selfs *, bahkan di dalam kelas ...

pengguna5554473
sumber
Penutupan baik untuk kasus penggunaan kecil, tetapi penggunaan yang diperpanjang akan meningkatkan overhead memori program cukup banyak (karena Anda menggunakan OO berbasis prototipe daripada OO berbasis kelas - sehingga setiap objek memerlukan serangkaian fungsi sendiri bukan menjaga seperangkat fungsi umum di kelas). Lebih jauh lagi, itu akan menghentikan Anda untuk dapat menggunakan metode sulap / dunder (misalnya __str__dan sejenisnya) karena ini dipanggil dengan cara yang berbeda dengan metode normal.
Dunes
0

Saya pikir akan lebih mudah dan lebih mudah dibaca jika ada pernyataan "anggota" sama seperti ada "global" sehingga Anda dapat memberi tahu penerjemah yang merupakan objek anggota kelas.

Pablo Villar
sumber