Apakah ada cara mudah untuk membuat acar fungsi python (atau membuat serial kodenya)?


100

Saya mencoba mentransfer fungsi melalui koneksi jaringan (menggunakan asyncore). Apakah ada cara mudah untuk membuat serial fungsi python (yang, setidaknya dalam kasus ini, tidak akan berpengaruh samping) untuk transfer seperti ini?

Idealnya saya ingin memiliki sepasang fungsi yang mirip dengan ini:

def transmit(func):
    obj = pickle.dumps(func)
    [send obj across the network]

def receive():
    [receive obj from the network]
    func = pickle.loads(s)
    func()

Jawaban:


120

Anda dapat membuat kode bytecode fungsi dan kemudian menyusunnya kembali pada pemanggil. The marshal modul dapat digunakan untuk benda kode deserialisasi, yang kemudian dapat kembali ke dalam fungsi. yaitu:

import marshal
def foo(x): return x*x
code_string = marshal.dumps(foo.func_code)

Kemudian dalam proses jarak jauh (setelah mentransfer code_string):

import marshal, types

code = marshal.loads(code_string)
func = types.FunctionType(code, globals(), "some_func_name")

func(10)  # gives 100

Beberapa peringatan:

  • format marshal (semua bytecode python dalam hal ini) mungkin tidak cocok antara versi python utama.

  • Hanya akan berfungsi untuk implementasi cpython.

  • Jika fungsi mereferensikan global (termasuk modul yang diimpor, fungsi lain, dll) yang perlu Anda ambil, Anda perlu membuat serial ini juga, atau membuatnya kembali di sisi jarak jauh. Contoh saya hanya memberikan namespace global proses jarak jauh.

  • Anda mungkin perlu melakukan sedikit lebih banyak untuk mendukung kasus yang lebih kompleks, seperti penutupan atau fungsi generator.


1
Dalam Python 2.5, modul "baru" tidak digunakan lagi. 'new.function' harus diganti dengan 'types.FunctionType', setelah "jenis impor", saya yakin.
Eric O Lebigot

2
Terima kasih. Inilah yang saya cari. Berdasarkan beberapa pengujian sepintas, ini berfungsi seperti untuk generator.
Michael Fairley

2
Jika Anda membaca beberapa paragraf pertama pada modul marshal, Anda melihatnya sangat menyarankan menggunakan acar sebagai gantinya? Sama untuk halaman acar. docs.python.org/2/library/marshal.html
dgorissen

1
Saya mencoba menerapkan marshalmodul untuk membuat serial kamus kamus yang diinisialisasi sebagai defaultdict(lambda : defaultdict(int)). Tapi itu mengembalikan kesalahan ValueError: unmarshallable object. Perhatikan bahwa saya menggunakan python2.7. Ada ide? Terima kasih
pengguna17375

2
Pada Python 3.5.3, foo.func_codekenaikan AttributeError. Apakah ada cara lain untuk mendapatkan kode fungsi?
AlQuemist

41

Lihat Dill , yang memperluas pustaka acar Python untuk mendukung lebih banyak jenis, termasuk fungsi:

>>> import dill as pickle
>>> def f(x): return x + 1
...
>>> g = pickle.dumps(f)
>>> f(1)
2
>>> pickle.loads(g)(1)
2

Ini juga mendukung referensi ke objek dalam penutupan fungsi:

>>> def plusTwo(x): return f(f(x))
...
>>> pickle.loads(pickle.dumps(plusTwo))(1)
3

2
dill juga melakukan pekerjaan yang cukup baik untuk mendapatkan kode sumber dari fungsi dan lambda dan menyimpannya ke disk, jika Anda lebih suka itu daripada pengawetan objek.
Mike McKerns

14

Saya harus tetap menggunakan pustaka standar untuk proyek khusus ini.
Michael Fairley

21
Tetapi itu tidak berarti Anda tidak dapat melihat kode Pyro untuk melihat bagaimana hal itu dilakukan :)
Aaron Digulla

4
@ AaronDigulla- true, tetapi perlu disebutkan bahwa sebelum membaca satu baris kode yang diterbitkan orang lain, Anda harus selalu memeriksa lisensi perangkat lunak. Membaca kode orang lain dan menggunakan kembali ide-ide tanpa mengutip sumber atau mengikuti batasan lisensi / penyalinan dapat dianggap plagiarisme dan / atau pelanggaran hak cipta dalam banyak kasus.
mdscruggs

12

Cara yang paling sederhana mungkin inspect.getsource(object)(lihat modul inspeksi ) yang mengembalikan String dengan kode sumber untuk suatu fungsi atau metode.


Ini terlihat bagus, kecuali bahwa nama fungsi didefinisikan secara eksplisit dalam kode, yang sedikit bermasalah. Saya bisa menghapus baris pertama kode, tapi itu bisa dipecahkan dengan melakukan sesuatu seperti 'def \ / n func ():'. Saya dapat membuat acar nama fungsi dengan fungsi itu sendiri, tetapi saya tidak memiliki jaminan bahwa nama tersebut tidak akan bertabrakan, atau saya harus meletakkan fungsi tersebut di pembungkus, yang masih bukan solusi terbersih, tetapi itu mungkin harus dilakukan.
Michael Fairley

1
Perhatikan bahwa modul inspect sebenarnya hanya menanyakan fungsi di mana ia didefinisikan, dan kemudian membaca baris tersebut dari file kode sumber - hampir tidak canggih.
terlalu banyak php

1
Anda bisa mengetahui nama fungsi dengan menggunakan atribut .__ name__. Anda dapat melakukan penggantian regex pada ^ def \ s * {name} \ s * (dan berikan nama apa pun yang Anda suka. Ini tidak sangat mudah, tetapi akan bekerja untuk banyak hal.
terlalu banyak php

6

Itu semua tergantung pada apakah Anda membuat fungsi saat runtime atau tidak:

Jika Anda melakukannya - inspect.getsource(object)tidak akan berfungsi untuk fungsi yang dibuat secara dinamis karena ia mendapatkan sumber objek dari .pyfile, jadi hanya fungsi yang ditentukan sebelum eksekusi yang dapat diambil sebagai sumber.

Dan jika fungsi Anda ditempatkan dalam file, mengapa tidak memberikan akses kepada penerima dan hanya menyebarkan nama modul dan fungsi.

Satu-satunya solusi untuk fungsi yang dibuat secara dinamis yang dapat saya pikirkan adalah membangun fungsi sebagai string sebelum transmisi, mengirimkan sumber, dan kemudian eval()di sisi penerima.

Sunting: marshalsolusinya terlihat juga cukup pintar, tidak tahu Anda dapat membuat serialisasi sesuatu yang lain yang ada di dalamnya



2
code_string = '' '
def foo (x):
    kembali x * 2
def bar (x):
    kembali x ** 2
'' '

obj = pickle.dumps (code_string)

Sekarang

exec (pickle.loads (obj))

foo (1)
> 2
batang (3)
> 9

2

Kamu bisa melakukan ini:

def fn_generator():
    def fn(x, y):
        return x + y
    return fn

Sekarang, transmit(fn_generator())akan mengirimkan definisi sebenarnya dari fn(x,y)alih-alih referensi ke nama modul.

Anda dapat menggunakan trik yang sama untuk mengirim kelas melalui jaringan.


1

Fungsi dasar yang digunakan untuk modul ini mencakup kueri Anda, ditambah lagi Anda mendapatkan kompresi terbaik; lihat kode sumber instruktif:

y_serial.py module :: gudang objek Python dengan SQLite

"Serialization + persistance :: dalam beberapa baris kode, kompres dan beri anotasi objek Python ke dalam SQLite; kemudian ambil secara kronologis dengan kata kunci tanpa SQL. Modul" standar "yang paling berguna untuk database untuk menyimpan data tanpa skema."

http://yserial.sourceforge.net


1

Cloudpickle mungkin adalah yang Anda cari. Cloudpickle dijelaskan sebagai berikut:

cloudpickle sangat berguna untuk komputasi cluster di mana kode Python dikirim melalui jaringan untuk dieksekusi pada host jarak jauh, mungkin dekat dengan data.

Contoh penggunaan:

def add_one(n):
  return n + 1

pickled_function = cloudpickle.dumps(add_one)
pickle.loads(pickled_function)(42)

0

Berikut adalah kelas helper yang dapat Anda gunakan untuk menggabungkan fungsi agar dapat dipilih. Peringatan yang telah disebutkan untuk marshalakan berlaku tetapi diusahakan untuk menggunakan acar bila memungkinkan. Tidak ada upaya yang dilakukan untuk melestarikan global atau penutupan di seluruh serialisasi.

    class PicklableFunction:
        def __init__(self, fun):
            self._fun = fun

        def __call__(self, *args, **kwargs):
            return self._fun(*args, **kwargs)

        def __getstate__(self):
            try:
                return pickle.dumps(self._fun)
            except Exception:
                return marshal.dumps((self._fun.__code__, self._fun.__name__))

        def __setstate__(self, state):
            try:
                self._fun = pickle.loads(state)
            except Exception:
                code, name = marshal.loads(state)
                self._fun = types.FunctionType(code, {}, name)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.