Apakah ini praktik yang lebih baik pra-inisialisasi atribut di kelas, atau menambahkannya di sepanjang jalan?

12

Saya minta maaf jika ini adalah pertanyaan yang benar-benar sangat mutakhir, tetapi saya ingin tahu apa praktik terbaik yang ada, dan sepertinya saya tidak dapat menemukan jawaban yang baik di Google.

Dalam Python, saya biasanya menggunakan kelas kosong sebagai wadah struktur data super-catchall (seperti file JSON), dan menambahkan atribut di sepanjang jalan:

class DataObj:
    "Catch-all data object"
    def __init__(self):
        pass

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

Ini memberi saya banyak fleksibilitas, karena objek kontainer pada dasarnya dapat menyimpan apa saja. Jadi jika persyaratan baru muncul, saya hanya akan menambahkannya sebagai atribut lain ke objek DataObj (yang saya berikan di kode saya).

Namun, baru-baru ini saya terkesan (oleh programmer FP) bahwa ini adalah praktik yang mengerikan, karena membuatnya sangat sulit untuk membaca kode. Kita harus melalui semua kode untuk mengetahui atribut apa yang sebenarnya dimiliki DataObj.

Pertanyaan : Bagaimana saya bisa menulis ulang ini untuk pemeliharaan yang lebih besar tanpa mengorbankan fleksibilitas?

Apakah ada ide dari pemrograman fungsional yang bisa saya adopsi?

Saya mencari praktik terbaik di luar sana.

Catatan : satu ide adalah untuk menginisialisasi kelas dengan semua atribut yang diharapkan akan ditemui, misalnya

class DataObj:
    "Catch-all data object"
    def __init__(self):
        data.a = 0
        data.b = ""
        data.c = []

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

Apakah ini ide yang bagus? Bagaimana jika saya tidak tahu atribut saya apriori?

Gilead
sumber
Struktur data Anda sangat bisa berubah-ubah sehingga Anda tampaknya peduli dengan kemampuan pemeliharaannya. Di waktu senggang Anda ™, coba baca artikel ini tentang model data yang tidak dapat diubah . Itu mungkin benar-benar mengubah cara Anda berpikir tentang data.
9000
@ 9000 Artikel seperti itu meyakinkan kembali yang sudah yakin. Bagi saya itu lebih seperti bagaimana-daripada dari mengapa (Daftar mengapa tidak benar-benar meyakinkan kecuali Anda merasa memiliki kebutuhan khusus). Bagi saya itu tidak meyakinkan seseorang memperbarui faktur di VB bahwa harus terus-menerus membuat salinan baru dari objek faktur mereka masuk akal (menambahkan pembayaran, objek faktur baru; tambahkan bagian, objek faktur baru).
Paul

Jawaban:

10

Bagaimana saya bisa menulis ulang ini untuk pemeliharaan yang lebih besar tanpa mengorbankan fleksibilitas?

Kamu tidak. Fleksibilitas inilah yang menyebabkan masalah. Jika ada kode di mana saja dapat mengubah atribut apa yang dimiliki objek, rawatan sudah terpotong-potong. Idealnya, setiap kelas memiliki seperangkat atribut yang ditetapkan setelahnya __init__dan sama untuk setiap instance. Tidak selalu mungkin atau masuk akal, tetapi itu harus terjadi setiap kali Anda tidak memiliki alasan yang bagus untuk menghindarinya.

satu ide adalah untuk menginisialisasi kelas dengan semua atribut yang diharapkan dapat ditemukan

Itu bukan ide yang bagus. Tentu, maka atributnya ada di sana, tetapi mungkin memiliki nilai palsu, atau bahkan valid yang menutupi kode yang tidak menetapkan nilai (atau salah eja). AttributeErrormenakutkan, tetapi mendapatkan hasil yang salah lebih buruk. Nilai default secara umum baik-baik saja, tetapi untuk memilih default yang masuk akal (dan memutuskan apa yang diperlukan), Anda perlu tahu untuk apa objek itu digunakan.

Bagaimana jika saya tidak tahu atribut saya apriori?

Maka Anda kacau dalam hal apa pun dan harus menggunakan dict atau daftar alih-alih nama atribut hardcoding. Tapi saya mengerti maksud Anda "... pada saat itu saya menulis kelas wadah". Maka jawabannya adalah: "Anda dapat mengedit file berbaris, ya." Perlu atribut baru? Tambahkan atribut frigging ke kelas kontainer. Ada lebih banyak kode yang menggunakan kelas itu dan tidak perlu atribut itu? Pertimbangkan untuk membagi berbagai hal dalam dua kelas terpisah (gunakan mixin untuk tetap KERING), jadi jadikan opsional jika masuk akal.

Jika Anda takut menulis kelas wadah berulang: Terapkan metaprogramming secara bijaksana, atau gunakan collections.namedtuplejika Anda tidak perlu bermutasi anggota setelah pembuatan (teman-teman FP Anda akan senang).


sumber
7

Anda selalu bisa menggunakan kelas Bunch Alex Martelli . Dalam kasus Anda:

class DataObj:
    "Catch-all data object"
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

def processData(inputs):
    data = DataObj(a=1, b="sym", c=[2,5,2,1])

Dengan begitu, setidaknya jelas bagi pembaca bahwa data hanyalah penyimpanan data yang bodoh dan orang dapat segera melihat nilai mana yang disimpan tanpa nama apa, karena semuanya terjadi dalam satu baris.

Dan ya, melakukan hal-hal seperti ini sebenarnya adalah ide yang bagus, kadang-kadang.

pembuat pil
sumber
1

Saya mungkin akan menggunakan pendekatan kedua, mungkin menggunakan Noneuntuk menunjukkan data yang tidak valid. Memang benar bahwa sulit untuk membaca / memelihara jika Anda menambahkan atribut nanti. Namun, informasi lebih lanjut tentang tujuan kelas / objek ini akan memberikan wawasan tentang mengapa ide pertama adalah desain yang buruk: di mana Anda akan memiliki kelas yang benar-benar kosong tanpa metode atau data default? Mengapa Anda tidak tahu atribut apa yang dimiliki kelas?

Ada kemungkinan yang processDatamungkin lebih baik sebagai metode ( process_datauntuk mengikuti konvensi penamaan python), karena bertindak atas kelas. Diberikan contoh, sepertinya itu mungkin lebih baik sebagai struktur data (di mana dictmungkin cukup).

Diberikan contoh nyata, Anda dapat mempertimbangkan membawa pertanyaan ke CodeReview , di mana mereka dapat membantu untuk memperbaiki kode.

Casey Kuball
sumber