2010-10-06 34 views
11

Trong Python, câu lệnh with được sử dụng để đảm bảo mã làm sạch luôn được gọi, bất kể ngoại lệ bị ném hoặc các cuộc gọi hàm trả về. Ví dụ:tương đương với chữ "có" của Ruby trong Ruby

with open("temp.txt", "w") as f: 
    f.write("hi") 
    raise ValueError("spitespite") 

Ở đây, tệp được đóng, mặc dù ngoại lệ được nêu ra. Giải thích tốt hơn là here.

Có tương đương với cấu trúc này trong Ruby không? Hoặc bạn có thể mã hóa một, kể từ khi Ruby có tiếp tục?

Trả lời

19

Ruby có hỗ trợ nhẹ về cú pháp cho quy trình ẩn danh bằng chữ (được gọi là khối trong Ruby). Do đó, nó không cần một tính năng ngôn ngữ mới cho việc này.

(Nói chung, đó là dấu hiệu xấu, nếu bạn cần thêm các tính năng ngôn ngữ. Bạn có thể triển khai mọi thứ trong thư viện, nếu không đó là dấu hiệu của thiết kế ngôn ngữ xấu.Vì vậy, những gì bạn thường làm là viết một phương thức nhận một khối mã, phân bổ tài nguyên, thực thi khối mã trong ngữ cảnh của tài nguyên đó và sau đó đóng tài nguyên.

Something như thế này:

def with(klass, *args) 
    yield r = klass.open(*args) 
ensure 
    r.close 
end 

Bạn có thể sử dụng nó như thế này:

with File, 'temp.txt', 'w' do |f| 
    f.write 'hi' 
    raise 'spitespite' 
end 

Tuy nhiên, đây là một cách rất thủ tục để làm điều này. Ruby là một ngôn ngữ hướng đối tượng, có nghĩa là trách nhiệm của thực hiện đúng một khối mã trong bối cảnh của một File phải thuộc về lớp File:

File.open 'temp.txt', 'w' do |f| 
    f.write 'hi' 
    raise 'spitespite' 
end 

Điều này có thể được thực hiện một cái gì đó như thế này:

def File.open(*args) 
    f = new(*args) 
    return f unless block_given? 
    yield f 
ensure 
    f.close if block_given? 
end 

Đây là mẫu chung được thực hiện bởi nhiều lớp trong thư viện lõi Ruby, thư viện chuẩn và thư viện của bên thứ ba.


Một tương ứng chặt chẽ hơn đối với các giao thức chung Python quản lý bối cảnh sẽ là:

def with(ctx) 
    yield ctx.setup 
ensure 
    ctx.teardown 
end 

class File 
    def setup; self end 
    alias_method :teardown, :close 
end 

with File.open('temp.txt', 'w') do |f| 
    f.write 'hi' 
    raise 'spitespite' 
end 

Lưu ý rằng đây là hầu như không thể phân biệt từ ví dụ Python, nhưng nó không đòi hỏi việc bổ sung các cú pháp mới với ngôn ngữ.

+0

yea tôi đồng ý đó là một dấu hiệu xấu khi ngôn ngữ của bạn cần các tính năng mới. lý do duy nhất bạn cần 'bằng' trong Python là bởi vì các chức năng ẩn danh bị suy giảm không có lý do. Bởi lược đồ logic đó là tốt nhất vì bạn có thể thực hiện các tính năng mới của mình bằng macro =) – Claudiu

+0

câu trả lời tuyệt vời –

+2

+1 Nó rất tuyệt có thể thêm điều này vào ngôn ngữ khi đang di chuyển (và với một lượng nhỏ mã như vậy) .Tôi nghĩ rằng cũng có một lợi thế của việc có một cú pháp phong phú trong ngôn ngữ đó.Tất cả các nhà phát triển Python đều biết những gì "với" có nghĩa là trong Python , và nó được ghi lại trên trang web/sách Python –

0

Bạn luôn có thể sử dụng khối try..catch..finally, trong đó phần finally chứa mã để dọn sạch.

Chỉnh sửa: xin lỗi, misspoke: bạn muốn begin..rescue..ensure.

+0

bạn cũng có thể thực hiện điều này bằng Python. với một tiện ích tốt đẹp, mặc dù, xem liên kết giải thích – Claudiu

+0

Xin lỗi, không quá nóng với Python. ;) AFAIK, không có cách viết tắt nào sử dụng các khối try/catch hiệu quả. Tuy nhiên, bạn nên sử dụng các khối. – mway

8

Tương đương trong Ruby sẽ chuyển một khối tới phương thức File.open.

File.open(...) do |file| 
    #do stuff with file 
end #file is closed 

Đây là thành ngữ mà Ruby sử dụng và thứ bạn nên cảm thấy thoải mái.

+0

đó là một trường hợp cụ thể cho việc sử dụng chính xác của 'với', nhưng tôi quan tâm đến việc xây dựng chung – Claudiu

+4

Tôi không biết Python rất tốt, nhưng nó trông giống như C# 'sử dụng' tuyên bố. Không có cú pháp cú pháp như thế trong Ruby, nhưng nhiều lớp thư viện thực hiện các phương thức như tôi đã trình bày ở trên. –

+1

Ngoại trừ đảm bảo tất nhiên, nhưng không có đường ở đó thực sự. –

1

Tôi sẽ chỉ thêm một số giải thích khác cho người khác; tín dụng nên đến với họ.

Thật vậy, trong Ruby, mã dọn dẹp là như những người khác đã nói, trong mệnh đề ensure; nhưng gói những thứ trong khối là phổ biến trong Ruby, và đây là cách nó được thực hiện hiệu quả nhất và nhất trong tinh thần của Ruby. Khi dịch, không dịch trực tiếp từng từ, bạn sẽ nhận được một số câu rất lạ. Tương tự như vậy, đừng mong đợi mọi thứ từ Python có sự tương ứng một đối một với Ruby.

Từ liên kết mà bạn đã đăng:

class controlled_execution: 
    def __enter__(self): 
     set things up 
     return thing 
    def __exit__(self, type, value, traceback): 
     tear things down 

with controlled_execution() as thing: 
    some code 

của Ruby bằng cách nào, một cái gì đó như thế này (người đàn ông, tôi có thể làm điều này hoàn toàn sai: D):

def controlled_executor 
    begin 
    do_setup 
    yield 
    ensure 
    do_cleanup 
    end 
end 

controlled_executor do ... 
    some_code 
end 

Rõ ràng, bạn có thể thêm đối số cho cả hai số controlled executor (được gọi theo kiểu thông thường) và để sinh lợi (trong trường hợp này bạn cần phải thêm đối số vào khối đó). Vì vậy, để thực hiện những gì bạn trích dẫn ở trên,

class File 
    def my_open(file, mode="r") 
    handle = open(file, mode) 
    begin 
     yield handle 
    ensure 
     handle.close 
    end 
    end 
end 

File.my_open("temp.txt", "w") do |f| 
    f.write("hi") 
    raise Exception.new("spitesprite") 
end 
+0

Tôi nghĩ rằng bạn đang ở trên một cái gì đó .. Tôi nghĩ rằng ruby ​​có khối có nghĩa là một tuyên bố "với" đặc biệt là không cần thiết. tôi không biết ruby ​​nhiều, do đó, chỉ là tạo ra một khối, vượt qua nó để controlled_executor, và được thực thi khi "năng suất" đạt được? – Claudiu

+0

Có. [15-char phụ] :) – Amadan

+0

Để viết nhiều hơn một chút - Ruby có các khối có nghĩa là nó có thể thực sự thực hiện những điều ngoài tường với cú pháp. Xem các ví dụ về Sinatra, RSpec và Rake để biết một số ý tưởng về cách sử dụng các khối sáng tạo. – Amadan

4

Bạn có thể sử dụng khối lập luận để làm điều này trong Ruby:

class Object 
    def with(obj) 
     obj.__enter__ 
     yield 
     obj.__exit__ 
    end 
end 

Bây giờ, bạn có thể thêm __enter____exit__ phương pháp để lớp khác và sử dụng nó như này:

with GetSomeObject("somefile.text") do |foo| 
    do_something_with(foo) 
end 
+0

hah thật tuyệt vời = P – Claudiu

+1

Bỏ qua điều đó bạn cần 'đảm bảo 'để nắm bắt các trường hợp đặc biệt, tôi vừa yêu vừa ghét giải pháp này. Nó cho thấy Ruby linh hoạt như thế nào (để nó gần như có thể tiếp cận cú pháp Python), và mặt khác ... Tại sao bạn muốn tiếp cận cú pháp Python trong Ruby? :) – Amadan

+1

vâng, đây là ngôn ngữ không có lý do gì. các câu trả lời khác tốt hơn. này si vẫn awesom tho – Claudiu

2

Có thể viết vào một tập tin nguyên tử trong Ruby, như vậy:

File.write("temp.txt", "hi") 
raise ValueError("spitespite") 

đang Viết như thế này có nghĩa là nó không thể vô tình để lại một tập tin mở.

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