Saya memiliki masalah dengan transfer variabel 'insurance_mode' oleh dekorator. Saya akan melakukannya dengan pernyataan dekorator berikut:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
tapi sayangnya, pernyataan ini tidak berfungsi. Mungkin mungkin ada cara yang lebih baik untuk menyelesaikan masalah ini.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
execute_complete_reservation
membutuhkan dua parameter, tetapi Anda melewatkannya satu. Dekorator hanyalah gula sintaksis untuk fungsi pembungkus di dalam fungsi lainnya. Lihat docs.python.org/reference/compound_stmts.html#fungsi untuk dokumentasi lengkap.Jawaban:
Sintaksis untuk dekorator dengan argumen sedikit berbeda - dekorator dengan argumen harus mengembalikan fungsi yang akan mengambil fungsi dan mengembalikan fungsi lainnya. Jadi harus benar-benar mengembalikan dekorator yang normal. Agak membingungkan, bukan? Yang saya maksud:
Di sini Anda dapat membaca lebih lanjut tentang subjek - juga dimungkinkan untuk menerapkan ini menggunakan objek yang dapat dipanggil dan itu juga dijelaskan di sana.
sumber
return function(*args, **kwargs)
@decorator()
dan bukan hanya@decorator
, bahkan jika Anda hanya memiliki argumen opsional.Sunting : untuk pemahaman mendalam tentang model mental dekorator, lihat Pycon Talk yang mengagumkan ini . layak 30 menit.
Salah satu cara berpikir tentang dekorator dengan argumen adalah
diterjemahkan menjadi
Jadi jika dekorator memiliki argumen,
diterjemahkan menjadi
decorator_with_args
adalah fungsi yang menerima argumen khusus dan yang mengembalikan dekorator yang sebenarnya (yang akan diterapkan ke fungsi yang didekorasi).Saya menggunakan trik sederhana dengan parsial untuk membuat dekorator saya mudah
Memperbarui:
Di atas,
foo
menjadireal_decorator(foo)
Salah satu efek dari mendekorasi suatu fungsi adalah bahwa namanya
foo
ditimpa pada deklarasi dekorator.foo
"diganti" oleh apa pun yang dikembalikan olehreal_decorator
. Dalam hal ini, objek fungsi baru.Semua
foo
metadata diganti, terutama nama fungsi dan docstring.functools.wraps memberi kita metode yang mudah untuk "mengangkat" docstring dan nama ke fungsi yang dikembalikan.
sumber
@functools.wraps
?functool.wraps
. Menambahkannya dalam contoh dapat membingungkan pembaca lebih lanjut.arg
sini !?bar
argumenreal_decorator
?Saya ingin menunjukkan ide yang IMHO cukup elegan. Solusi yang diajukan oleh t.dubrownik menunjukkan pola yang selalu sama: Anda membutuhkan pembungkus tiga lapis terlepas dari apa yang dilakukan dekorator.
Jadi saya pikir ini adalah pekerjaan untuk dekorator meta, yaitu dekorator untuk dekorator. Karena dekorator adalah fungsi, sebenarnya dekorator berfungsi sebagai dekorator biasa dengan argumen:
Ini dapat diterapkan pada dekorator biasa untuk menambah parameter. Jadi misalnya, misalkan kita memiliki dekorator yang menggandakan hasil fungsi:
Dengan
@parametrized
kita dapat membangun@multiply
dekorator generik yang memiliki parameterSecara konvensional parameter pertama dari dekorator parametrized adalah fungsinya, sedangkan argumen yang tersisa akan sesuai dengan parameter dekorator parametrized.
Contoh penggunaan yang menarik bisa menjadi dekorator asertif tipe-aman:
Catatan terakhir: di sini saya tidak menggunakan
functools.wraps
untuk fungsi wrapper, tapi saya akan merekomendasikan menggunakannya setiap saat.sumber
@wraps
saya untuk kasus khusus saya.@parametrized
trik Anda . Masalah yang saya miliki adalah saya lupa@
sintaksnya sama dengan panggilan sebenarnya (entah bagaimana saya tahu itu dan tidak tahu bahwa pada saat yang sama Anda dapat mengumpulkan dari pertanyaan saya). Jadi, jika Anda ingin menerjemahkan@
sintaks menjadi panggilan biasa untuk memeriksa cara kerjanya, Anda lebih baik mengomentariya sementara atau Anda akhirnya akan memanggilnya dua kali dan mendapatkan hasilIni adalah versi yang sedikit dimodifikasi dari jawaban t.dubrownik . Mengapa?
Jadi gunakan
@functools.wraps()
:sumber
Saya kira masalah Anda adalah memberikan argumen kepada dekorator Anda. Ini sedikit rumit dan tidak langsung.
Berikut ini contoh cara melakukannya:
Cetakan:
Lihat artikel Bruce Eckel untuk lebih jelasnya.
sumber
__name__
yang tidak dimiliki instance dari kelas dekorator?class Foo: @MyDec(...) def method(self, ...): blah
yang tidak berfungsi karenaFoo().method
tidak akan menjadi metode terikat dan tidak akan lulusself
secara otomatis. Ini juga dapat diperbaiki, dengan membuatMyDec
deskriptor dan membuat metode terikat__get__
, tetapi lebih terlibat dan jauh lebih jelas. Pada akhirnya, kelas dekorator tidak senyaman yang terlihat.Penggunaan dekorator
Lalu
menghasilkan
tapi
menghasilkan
sumber
Ini adalah templat untuk dekorator fungsi yang tidak memerlukan
()
jika tidak ada parameter yang diberikan:contoh dari ini diberikan di bawah ini:
sumber
factor_or_func
(atau parameter lainnya) tidak boleh dipindahkan kewrapper()
.locals()
?()
.Dalam contoh saya, saya memutuskan untuk menyelesaikan ini melalui lambda satu baris untuk membuat fungsi dekorator baru:
Ketika dieksekusi, ini mencetak:
Mungkin tidak bisa diperluas seperti solusi lain, tetapi berhasil untuk saya.
sumber
Menulis dekorator yang berfungsi dengan dan tanpa parameter adalah tantangan karena Python mengharapkan perilaku yang sama sekali berbeda dalam dua kasus ini! Banyak jawaban telah mencoba untuk mengatasi ini dan di bawah ini merupakan peningkatan dari jawaban oleh @ norok2. Secara khusus, variasi ini menghilangkan penggunaan
locals()
.Berikut contoh yang sama seperti yang diberikan oleh @ norok2:
Mainkan dengan kode ini .
Tangkapannya adalah bahwa pengguna harus menyediakan kunci, nilai pasangan parameter, bukan parameter posisi dan parameter pertama dicadangkan.
sumber
Sudah diketahui umum bahwa dua bagian kode berikut ini hampir setara:
Kesalahan umum adalah berpikir bahwa
@
hanya menyembunyikan argumen paling kiri.Akan jauh lebih mudah untuk menulis dekorator jika di atas adalah cara
@
kerjanya. Sayangnya, bukan itu yang dilakukan.Pertimbangkan dekorator
Wait
yang menghambat eksekusi program selama beberapa detik. Jika Anda tidak melewatkan waktu tunggu maka nilai default adalah 1 detik. Kasus penggunaan ditunjukkan di bawah ini.Ketika
Wait
memiliki argumen, seperti@Wait(3)
, maka panggilanWait(3)
dieksekusi sebelum hal lain terjadi.Artinya, dua potong kode berikut ini setara
Ini adalah sebuah masalah.
Satu solusi ditunjukkan di bawah ini:
Mari kita mulai dengan membuat kelas berikut,
DelayedDecorator
:Sekarang kita dapat menulis hal-hal seperti:
Perhatikan bahwa:
dec
tidak menerima banyak argumen.dec
hanya menerima fungsi yang akan dibungkus.import inspect class PolyArgDecoratorMeta (type): def call (Tunggu, * args, ** kwargs): coba: arg_count = len (args) if (arg_count == 1): jika dapat dipanggil (args [0]): SuperClass = inspect. getmro (PolyArgDecoratorMeta) [1] r = SuperClass. panggil (Tunggu, args [0]) lain: r = DelayedDecorator (Tunggu, * args, ** kwargs) else: r = DelayedDecorator (Tunggu, * args, ** kwargs) akhirnya: pass return r
import time class Tunggu (metaclass = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = delay
Dua potong kode berikut ini setara:
Kami dapat mencetak
"something"
ke konsol dengan sangat lambat, sebagai berikut:Catatan Akhir
Ini mungkin terlihat seperti banyak kode, tetapi Anda tidak harus menulis kelas
DelayedDecorator
danPolyArgDecoratorMeta
setiap waktu. Satu-satunya kode yang harus Anda tulis sendiri adalah sebagai berikut, yang cukup singkat:sumber
tentukan "fungsi penghias ini" untuk menghasilkan fungsi penghias yang disesuaikan:
gunakan seperti ini:
sumber
Jawaban bagus di atas. Yang ini juga menggambarkan
@wraps
, yang mengambil string doc dan nama fungsi dari fungsi asli dan menerapkannya ke versi terbungkus baru:Cetakan:
sumber
Jika fungsi dan dekorator harus mengambil argumen, Anda dapat mengikuti pendekatan di bawah ini.
Misalnya ada dekorator bernama
decorator1
yang mengambil argumenSekarang jika
decorator1
argumen harus dinamis, atau diteruskan saat memanggil fungsi,Dalam kode di atas
seconds
adalah argumen untukdecorator1
a, b
adalah argumen darifunc1
sumber