Có cách nào dễ dàng để chọn một hàm python (hoặc cách khác là tuần tự hóa mã của nó) không?


100

Tôi đang cố chuyển một chức năng qua kết nối mạng (sử dụng asyncore). Có cách nào dễ dàng để tuần tự hóa một hàm python (ít nhất là trong trường hợp này, sẽ không có tác động phụ nào) để chuyển như thế này không?

Lý tưởng nhất là tôi muốn có một cặp hàm tương tự như sau:

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

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

Câu trả lời:


120

Bạn có thể tuần tự hóa hàm bytecode và sau đó cấu trúc lại nó trên trình gọi. Các soái mô-đun có thể được sử dụng để đối tượng đang tự các, mà sau đó có thể được tập hợp lại thành một hàm. I E:

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

Sau đó, trong quá trình từ xa (sau khi chuyển mã_string):

import marshal, types

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

func(10)  # gives 100

Một số lưu ý:

  • Định dạng của soái ca (bất kỳ mã bytecode nào của python cho vấn đề đó) có thể không tương thích giữa các phiên bản python chính.

  • Sẽ chỉ hoạt động cho việc triển khai cpython.

  • Nếu hàm tham chiếu đến toàn cầu (bao gồm các mô-đun đã nhập, các hàm khác, v.v.) mà bạn cần lấy, bạn cũng cần phải tuần tự hóa chúng hoặc tạo lại chúng ở phía từ xa. Ví dụ của tôi chỉ cung cấp cho nó không gian tên chung của quy trình từ xa.

  • Có thể bạn sẽ cần phải làm nhiều hơn một chút để hỗ trợ các trường hợp phức tạp hơn, chẳng hạn như các hàm đóng hoặc trình tạo.


1
Trong Python 2.5, mô-đun "mới" không được dùng nữa. Tôi tin rằng 'new. functions' nên được thay thế bằng 'styles.FunctionType', sau một "loại nhập khẩu".
Eric O Lebigot

2
Cảm ơn. Điều này thật đúng với gì mà tôi đã tìm kiếm. Dựa trên một số thử nghiệm lướt qua, nó hoạt động như đối với máy phát điện.
Michael Fairley

2
Nếu bạn đọc một vài đoạn đầu tiên trên mô-đun soái ca, bạn thấy nó thực sự đề xuất sử dụng dưa chua thay thế? Tương tự đối với trang dưa chua. docs.python.org/2/library/marshal.html
dgorissen

1
Tôi đang cố gắng áp dụng marshalmô-đun để tuần tự hóa một kho từ điển được khởi tạo dưới dạng defaultdict(lambda : defaultdict(int)). Nhưng nó trả về lỗi ValueError: unmarshallable object. Lưu ý tôi là usin python2.7. Bất kỳ ý tưởng? Cảm ơn
dùng17375

2
Trên Python 3.5.3, foo.func_codetăng AttributeError. Có cách nào khác để lấy mã chức năng không?
AlQuemist

41

Hãy xem Dill , mở rộng thư viện dưa chua của Python để hỗ trợ nhiều loại hơn, bao gồm các hàm:

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

Nó cũng hỗ trợ các tham chiếu đến các đối tượng trong bao hàm của hàm:

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

2
thì dill cũng thực hiện một công việc khá tốt trong việc lấy mã nguồn từ các hàm và lambdas và lưu chúng vào đĩa, nếu bạn muốn điều đó hơn là chọn đối tượng.
Mike McKern

14

Tôi cần gắn bó với thư viện tiêu chuẩn cho dự án cụ thể này.
Michael Fairley

21
Nhưng điều đó không có nghĩa là bạn không thể trông vào mã của Pyro để xem làm thế nào nó được thực hiện :)
Aaron Digulla

4
@ AaronDigulla- true, nhưng điều đáng nói là trước khi đọc một dòng mã đã xuất bản của người khác, bạn phải luôn kiểm tra giấy phép của phần mềm. Việc đọc mã của người khác và sử dụng lại ý tưởng mà không trích dẫn nguồn hoặc tuân theo các ràng buộc về giấy phép / sao chép có thể bị coi là đạo văn và / hoặc vi phạm bản quyền trong nhiều trường hợp.
mdscruggs

12

Cách đơn giản nhất có lẽ là inspect.getsource(object)(xem mô-đun kiểm tra ) trả về một Chuỗi có mã nguồn cho một hàm hoặc một phương thức.


Điều này có vẻ tốt, ngoại trừ việc tên hàm được xác định rõ ràng trong mã, điều này hơi có vấn đề. Tôi có thể loại bỏ dòng đầu tiên của mã, nhưng điều đó có thể bị phá vỡ bằng cách làm một cái gì đó như 'def \ / n func ():'. Tôi có thể chọn tên của hàm với chính hàm đó, nhưng tôi không có gì đảm bảo rằng tên đó sẽ không đụng nhau hoặc tôi phải đặt hàm trong một trình bao bọc, đây vẫn không phải là giải pháp sạch sẽ nhất, nhưng nó có thể phải làm.
Michael Fairley

1
Lưu ý rằng mô-đun kiểm tra thực sự chỉ hỏi chức năng nơi nó được xác định và sau đó đọc các dòng đó từ tệp mã nguồn - hầu như không phức tạp.
quá nhiều php

1
Bạn có thể tìm ra tên của hàm bằng thuộc tính .__ name__ của nó. Bạn có thể làm một regex thay thế trên ^ def \ s * {name} \ s * (và cung cấp cho nó bất cứ điều gì tên bạn như Nó không đơn giản, nhưng nó sẽ làm việc cho hầu hết mọi thứ..
quá nhiều php

6

Tất cả phụ thuộc vào việc bạn có tạo hàm trong thời gian chạy hay không:

Nếu bạn làm vậy - inspect.getsource(object)sẽ không hoạt động đối với các hàm được tạo động vì nó lấy nguồn của đối tượng từ .pytệp, vì vậy chỉ những hàm được xác định trước khi thực thi mới có thể được truy xuất làm nguồn.

Và nếu các chức năng của bạn vẫn được đặt trong tệp, tại sao không cấp cho người nhận quyền truy cập vào chúng và chỉ chuyển xung quanh tên mô-đun và chức năng.

Giải pháp duy nhất cho các hàm được tạo động mà tôi có thể nghĩ đến là xây dựng hàm dưới dạng một chuỗi trước khi truyền, nguồn phát và sau đó là eval()nó ở phía thu.

Chỉnh sửa: marshalgiải pháp trông cũng khá thông minh, không biết bạn có thể tuần tự hóa thứ gì đó khác mà không được tích hợp sẵn



2
code_string = '' '
def foo (x):
    trả về x * 2
thanh def (x):
    trả về x ** 2
''

obj = pickle.dumps (code_string)

Hiện nay

executive (pickle.loads (obj))

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

2

Bạn có thể làm được việc này:

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

Bây giờ, transmit(fn_generator())sẽ gửi definiton thực fn(x,y)thay vì tham chiếu đến tên mô-đun.

Bạn có thể sử dụng thủ thuật tương tự để gửi các lớp học qua mạng.


1

Các chức năng cơ bản được sử dụng cho mô-đun này bao gồm truy vấn của bạn, ngoài ra, bạn sẽ có được khả năng nén tốt nhất qua dây; xem mã nguồn hướng dẫn:

y_serial.py mô-đun :: kho các đối tượng Python với SQLite

"Tuần tự hóa + kiên trì :: trong một vài dòng mã, nén và chú thích các đối tượng Python vào SQLite; sau đó truy xuất chúng theo thứ tự thời gian bằng các từ khóa mà không cần bất kỳ SQL nào. Mô-đun" tiêu chuẩn "hữu ích nhất cho cơ sở dữ liệu để lưu trữ dữ liệu ít giản đồ."

http://yserial.sourceforge.net


1

Cloudpickle có lẽ là thứ bạn đang tìm kiếm. Cloudpickle được mô tả như sau:

cloudpickle đặc biệt hữu ích cho điện toán cụm trong đó mã Python được vận chuyển qua mạng để thực thi trên các máy chủ từ xa, có thể gần với dữ liệu.

Ví dụ sử dụng:

def add_one(n):
  return n + 1

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

0

Đây là một lớp trợ giúp bạn có thể sử dụng để bọc các hàm nhằm làm cho chúng có thể nhặt được. Các lưu ý đã được đề cập marshalsẽ được áp dụng nhưng chúng tôi sẽ cố gắng sử dụng dưa chua bất cứ khi nào có thể. Không có nỗ lực nào được thực hiện để bảo vệ các hình cầu hoặc các kết thúc trong quá trình tuần tự hóa.

    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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.