7

Để tạo ra một đầu ra, một hàm thường chỉ sử dụng các giá trị của các đối số của nó. Tuy nhiên, cũng có những trường hợp trong đó chức năng, để tạo ra đầu ra của nó, đọc một cái gì đó từ một hệ thống tập tin hoặc từ một cơ sở dữ liệu hoặc từ web. Tôi muốn có một cách đơn giản và đáng tin cậy để đảm bảo rằng một cái gì đó như thế không xảy ra.Làm thế nào để đảm bảo rằng một hàm python tạo ra đầu ra của nó chỉ dựa trên đầu vào của nó?

Một cách mà tôi thấy là tạo danh sách trắng các thư viện python có thể được sử dụng để đọc từ hệ thống tệp, cơ sở dữ liệu hoặc web. Nhưng nếu đó là con đường để đi, nơi tôi có thể nhận được danh sách này (tiềm năng rất lớn). Hơn nữa, tôi không muốn vô hiệu hóa toàn bộ thư viện chỉ vì nó có thể được sử dụng để đọc từ hệ thống tập tin. Ví dụ tôi muốn người dùng có thể sử dụng thư viện gấu trúc (để lưu trữ và thao tác dữ liệu bảng). Tôi chỉ không muốn họ có thể sử dụng thư viện này để đọc dữ liệu từ hệ thống tệp.

Có giải pháp cho vấn đề này không?

+1

Sao lưu một bước. * Tại sao * bạn có muốn ngăn ai đó đọc từ một nguồn bên ngoài? – chepner

+0

Có nhiều lý do. Trước hết, tôi muốn chắc chắn rằng trong tương lai chức năng sẽ tạo ra cùng một đầu ra như ngày hôm nay. Thứ hai, nói chung, tôi nghĩ rằng đó là một giải pháp "xấu xí" khi một hàm nào đó đọc một cái gì đó. Nó sẽ chỉ thấy những gì nó nhận rõ ràng như đầu vào. Nếu một cái gì đó nên được đọc từ một tập tin hoặc cơ sở dữ liệu, nó sẽ được đọc bên ngoài chức năng và được chuyển đến chức năng như một trong các đầu vào của nó. – Roman

+0

Vì vậy, bạn muốn sử dụng mã mà bạn không tin tưởng? –

Trả lời

8

Câu trả lời cho câu hỏi này là không. Những gì bạn đang tìm kiếm là một hàm kiểm tra cho functional purity. Tuy nhiên, như đã thể hiện trong mã này, không có cách nào để đảm bảo rằng không có tác dụng phụ nào thực sự được gọi.

class Foo(object): 
    def __init__(self, x): 
     self.x = x 
    def __add__(self, y): 
     print("HAHAHA evil side effects here...") 
     # proceed to read a file and do stuff 
     return self 

# this looks pure... 
def f(x): return x + 1 

# but really... 
>>> f(Foo(1)) 
HAHAHA evil side effects here... 

Vì các đối tượng toàn diện có thể xác định lại hành vi của chúng (truy cập trường, gọi điện, quá tải toán tử, v.v.), bạn luôn có thể truyền đầu vào. Do đó, các hàm thuần túy duy nhất là các hàm thực sự không làm gì với các đối số của chúng ... một lớp các hàm thường hữu ích hơn.

Tất nhiên, nếu bạn có thể chỉ định các hạn chế khác, điều này trở nên dễ dàng hơn.

+0

Trong ví dụ của bạn "hiệu ứng ác" xảy ra bởi vì người sử dụng chức năng đã làm một cái gì đó "xấu" (người dùng được gọi là một "tốt" chức năng với một "xấu" đối số). Trong trường hợp của tôi, tôi là người dùng của hàm. Vì vậy, tôi sẽ không gọi các chức năng theo cách "xấu". Tôi chỉ cần chắc chắn rằng các chức năng mà tôi sử dụng không phải là "xấu". – Roman

+0

Điều quan trọng là phải biết trước thời hạn ... – PythonNut

+2

@Roman: Yêu cầu của bạn ban đầu mạnh hơn rất nhiều. Bạn đã viết: "Tôi muốn người dùng có thể ... Tôi chỉ không muốn họ có thể sử dụng thư viện này để đọc dữ liệu từ hệ thống tệp". và bây giờ bạn đang viết "Tôi sẽ không gọi các chức năng một cách xấu." Điều đó có vẻ bất thường. Bạn tin rằng người dùng, nhưng không tin rằng phần mềm được cài đặt? – hynekcer

4

Các hạn chế bắt buộc của bạn có thể bị hỏng ngay cả khi bạn xóa tất cả các mô-đun và tất cả các chức năng. Mã có thể nhận được quyền truy cập vào các tệp, nếu nó có thể sử dụng các thuộc tính của một đối tượng đơn giản tùy ý, ví dụ: của số không.

(0).__class__.__base__.__subclasses__()[40]('/etc/pas'+'swd') 

Chỉ số 40 là cá nhân và rất điển hình cho Python 2.7, nhưng chỉ số của lớp con <type 'file'> có thể dễ dàng tìm thấy:

[x for x in (1).__class__.__base__.__subclasses__()if'fi'+'le'in'%s'%x][0](
'/etc/pas'+'swd') 

Bất kỳ sự kết hợp của danh sách trắng và danh sách đen là một trong hai không an toàn và/hoặc quá hạn chế. Các pypy sandbox là mạnh mẽ bởi các nguyên tắc mà không cần thỏa hiệp:

... subprocess này có thể chạy độc đoán mã Python không tin cậy, nhưng tất cả đầu vào của nó/đầu ra là serialized vào một đường ống stdin/stdout thay vì trực tiếp thực hiện. Quá trình bên ngoài đọc các đường ống và quyết định mà lệnh được phép hay không (sandboxing), hoặc thậm chí giải thích lại cách khác nhau ...

Cũng là một giải pháp dựa trên tính năng kernel seccomp có thể đủ an toàn. (blog)


Tôi muốn chắc chắn rằng trong tương lai chức năng sẽ tạo ra cùng một sản lượng như ngày hôm nay.

Nó rất dễ dàng để viết một hàm có cứng kết quả tái sản xuất và nó không thể dễ dàng ngăn chặn:

class A(object): 
    "This can be any very simple class" 
    def __init__(self, x): 
     self.x = x 
    def __repr__(self): 
     return repr(self.x) 

def strange_function(): 
    # You get a different result probably everytimes. 
    return list(set(A(i) for i in range(20))) 

>>> strange_function() 
[1, 18, 12, 5, 16, 15, 8, 2, 14, 0, 6, 19, 13, 11, 10, 9, 17, 3, 7, 4] 
>>> strange_function() 
[0, 9, 14, 3, 17, 5, 6, 11, 8, 1, 15, 7, 12, 13, 2, 10, 16, 4, 19, 18] 

... thậm chí nếu bạn loại bỏ everythng mà phụ thuộc vào thời gian, số ngẫu nhiên máy phát điện, thứ tự dựa trên chức năng băm vv, nó cũng dễ dàng để viết một chức năng mà đôi khi vượt quá bộ nhớ có sẵn hoặc giới hạn thời gian chờ và đôi khi cho kết quả.


EDIT:
La Mã, bạn viết gần đây mà bạn chắc chắn có thể tin người dùng. Sau đó, một giải pháp thực tế tồn tại. Nó là để xác minh đầu vào và đầu ra từ một hàm bằng cách ghi nó vào một tệp và xác minh nó trên máy ảo chạy từ xa IPython notebook (video ngắn hướng dẫn ngắn gọn, hỗ trợ tính toán từ xa, khởi động lại dịch vụ phụ trợ bằng web trình đơn tài liệu từ trình duyệt trong một giây, mà không làm mất dữ liệu (đầu vào/đầu ra) trong sổ ghi chép (tài liệu html) vì nó được tạo theo từng bước bởi hoạt động của chúng tôi kích hoạt javascript gọi trình điều khiển từ xa).

Bạn không cần quan tâm đến cuộc gọi nội bộ, chỉ có đầu vào và đầu ra toàn cục, cho đến khi bạn tìm thấy sự khác biệt. Máy ảo sẽ có thể xác minh kết quả độc lập và có thể tái sản xuất. Cấu hình tường lửa mà máy chấp nhận các kết nối từ bạn, nhưng không thể khởi tạo kết nối gửi đi. Cấu hình hệ thống tập tin mà không có dữ liệu nào có thể được lưu bởi người dùng hiện tại và do đó chúng không có mặt, ngoại trừ các thành phần phần mềm. Tắt dịch vụ cơ sở dữ liệu. Xác minh kết quả đầu vào/đầu ra theo thứ tự ngẫu nhiên hoặc khởi động hai dịch vụ sổ tay IPython trên các cổng khác nhau và chọn một phụ trợ ngẫu nhiên cho mỗi dòng lệnh trên sổ ghi chép, hoặc khởi động lại tiến trình phụ trợ thường xuyên trước bất cứ điều gì quan trọng. Nếu bạn tìm thấy sự khác biệt, hãy gỡ lỗi mã của bạn và sửa nó.

Bạn có thể tự động hóa nó mà không có "sổ tay" cuối cùng chỉ với tính toán từ xa IPython sau khi bạn không cần tương tác.

+1

Cần lưu ý rằng bạn cũng có thể lấy số ngẫu nhiên từ địa chỉ bộ nhớ ngẫu nhiên của một đối tượng. 'class A: pass; str (A)' – PythonNut

4

Điều bạn muốn được gọi là hộp cát hoặc Python bị giới hạn.

Cả hai phần lớn đều chết.

Gần nhất với chức năng ngày hôm nay là http://pypy.readthedocs.org/en/latest/sandbox.html lưu ý tuy nhiên, bản dựng mới nhất thực sự là 3 tuổi.

Các vấn đề liên quan