Bagaimana mengimplementasikan yang setara dengan a __getattr__
di kelas, di modul?
Contoh
Saat memanggil fungsi yang tidak ada dalam atribut modul yang didefinisikan secara statis, saya ingin membuat instance kelas dalam modul itu, dan memanggil metode di atasnya dengan nama yang sama seperti yang gagal dalam pencarian atribut pada modul.
class A(object):
def salutation(self, accusative):
print "hello", accusative
# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
return getattr(A(), name)
if __name__ == "__main__":
# i hope here to have my __getattr__ function above invoked, since
# salutation does not exist in the current namespace
salutation("world")
Pemberian yang mana:
matt@stanley:~/Desktop$ python getattrmod.py
Traceback (most recent call last):
File "getattrmod.py", line 9, in <module>
salutation("world")
NameError: name 'salutation' is not defined
python
module
python-3.x
getattr
attributeerror
Matt Joiner
sumber
sumber
salutation
ada di namespace global atau lokal (yang coba dilakukan oleh kode di atas) atau Anda ingin pencarian nama yang dinamis saat membuat akses titik pada modul? Itu dua hal yang berbeda.__getattr__
pada modul didukung dari Python 3.7Jawaban:
Beberapa waktu lalu, Guido mendeklarasikan bahwa semua pencarian metode khusus pada kelas gaya baru melewati
__getattr__
dan__getattribute__
. Metode Dunder sebelumnya bekerja pada modul - Anda dapat, misalnya, menggunakan modul sebagai pengelola konteks hanya dengan mendefinisikan__enter__
dan__exit__
, sebelum trik tersebut rusak .Baru-baru ini beberapa fitur historis telah kembali, modul
__getattr__
di antaranya, dan peretasan yang ada (modul yang menggantikan dirinya dengan kelas padasys.modules
saat impor) seharusnya tidak lagi diperlukan.Di Python 3.7+, Anda cukup menggunakan satu cara yang jelas. Untuk menyesuaikan akses atribut pada modul, tentukan
__getattr__
fungsi pada tingkat modul yang harus menerima satu argumen (nama atribut), dan mengembalikan nilai yang dihitung atau meningkatkanAttributeError
:Ini juga akan memungkinkan kait ke impor "dari", yaitu Anda dapat mengembalikan objek yang dibuat secara dinamis untuk pernyataan seperti
from my_module import whatever
.Pada catatan terkait, bersama dengan getattr modul, Anda juga dapat menentukan
__dir__
fungsi di tingkat modul untuk meresponsdir(my_module)
. Lihat PEP 562 untuk detailnya.sumber
m = ModuleType("mod")
dan setm.__getattr__ = lambda attr: return "foo"
; Namun, ketika saya larifrom mod import foo
, saya mengertiTypeError: 'module' object is not iterable
.m.__getattr__ = lambda attr: "foo"
, ditambah Anda perlu mendefinisikan entri untuk modul dengansys.modules['mod'] = m
. Setelah itu, tidak ada kesalahan denganfrom mod import foo
.my_module.whatever
untuk memanggilnya (setelah sebuahimport my_module
).Ada dua masalah dasar yang Anda hadapi di sini:
__xxx__
metode hanya dicari di kelasTypeError: can't set attributes of built-in/extension type 'module'
(1) berarti solusi apa pun juga harus melacak modul mana yang sedang diperiksa, jika tidak, setiap modul akan memiliki perilaku substitusi-instance; dan (2) berarti bahwa (1) bahkan tidak mungkin ... setidaknya tidak secara langsung.
Untungnya, sys.modules tidak pilih-pilih tentang apa yang ada di sana sehingga pembungkus akan berfungsi, tetapi hanya untuk akses modul (yaitu
import somemodule; somemodule.salutation('world')
; untuk akses modul yang sama Anda harus mencabut metode dari kelas substitusi dan menambahkannya keglobals()
eiher dengan metode kustom di kelas (saya suka menggunakan.export()
) atau dengan fungsi umum (seperti yang sudah terdaftar sebagai jawaban). Satu hal yang perlu diingat: jika pembungkus selalu membuat instance baru, dan solusi global tidak, Anda berakhir dengan perilaku yang agak berbeda. Oh, dan Anda tidak dapat menggunakan keduanya pada saat yang sama - itu salah satu.Memperbarui
Dari Guido van Rossum :
Jadi cara yang mapan untuk mencapai apa yang Anda inginkan adalah dengan membuat satu kelas dalam modul Anda, dan sebagai tindakan terakhir modul ganti
sys.modules[__name__]
dengan instance kelas Anda - dan sekarang Anda dapat bermain dengan__getattr__
/__setattr__
/__getattribute__
sesuai kebutuhan.Catatan 1 : Jika Anda menggunakan fungsionalitas ini, apa pun yang ada di modul, seperti global, fungsi lain, dll., Akan hilang saat
sys.modules
tugas dibuat - jadi pastikan semua yang diperlukan ada di dalam kelas pengganti.Catatan 2 : Untuk mendukung
from module import *
Anda harus__all__
didefinisikan di kelas; sebagai contoh:Bergantung pada versi Python Anda, mungkin ada nama lain untuk dihilangkan
__all__
. Theset()
dapat dihilangkan jika Python 2 kompatibilitas tidak diperlukan.sumber
import sys
memberiNone
untuksys
. Saya menduga peretasan ini tidak dikenai sanksi dalam Python 2.Ini adalah hack, tetapi Anda dapat membungkus modul dengan kelas:
sumber
import *
.Kami biasanya tidak melakukannya seperti itu.
Yang kami lakukan adalah ini.
Mengapa? Sehingga contoh global implisit terlihat.
Misalnya, lihat
random
modul, yang membuat instance global implisit untuk sedikit menyederhanakan kasus penggunaan di mana Anda menginginkan generator nomor acak "sederhana".sumber
Mirip dengan apa yang @ Håvard S usulkan, dalam kasus di mana saya perlu menerapkan beberapa sihir pada modul (seperti
__getattr__
), saya akan mendefinisikan kelas baru yang mewarisitypes.ModuleType
dan memasukkannyasys.modules
(mungkin mengganti modul tempat kebiasaan sayaModuleType
didefinisikan).Lihat
__init__.py
file utama Werkzeug untuk implementasi yang cukup kuat dari ini.sumber
Ini hackish, tapi ...
Ini bekerja dengan mengulang semua objek di namespace global. Jika item tersebut adalah kelas, item tersebut diulangi atas atribut kelas. Jika atribut dapat dipanggil, ia menambahkannya ke namespace global sebagai fungsi.
Ini mengabaikan semua atribut yang berisi "__".
Saya tidak akan menggunakan ini dalam kode produksi, tetapi ini akan membantu Anda memulai.
sumber
AddGlobalAttribute()
ketika ada level modulAttributeError
- jenis kebalikan dari logika @ Håvard S. Jika saya memiliki kesempatan, saya akan menguji ini dan menambahkan jawaban hybrid saya sendiri meskipun OP telah menerima jawaban ini.NameError
pengecualian untuk namespace (modul) global - yang menjelaskan mengapa jawaban ini menambahkan callable untuk setiap kemungkinan yang ditemukannya ke namespace global saat ini untuk mencakup setiap kemungkinan kasus sebelumnya.Inilah kontribusi saya yang sederhana - sedikit hiasan dari jawaban @ Håvard S yang berperingkat tinggi, tetapi sedikit lebih eksplisit (jadi mungkin dapat diterima oleh @ S. Lott, meskipun mungkin tidak cukup baik untuk OP):
sumber
Buat file modul Anda yang memiliki kelas Anda. Impor modul. Jalankan
getattr
pada modul yang baru saja Anda impor. Anda dapat melakukan impor dinamis menggunakan__import__
dan menarik modul dari sys.modules.Ini modul Anda
some_module.py
:Dan di modul lain:
Melakukan ini secara dinamis:
sumber