Nếu bạn nghĩ về nó, bên cạnh thực tế là đối số biến giải nén tất cả các gói cùng một lúc, cũng có một thực tế là format
không nhất thiết phải lấy đối số của nó theo thứ tự, như trong '{2} {1} {0}'
.
Bạn có thể làm việc xung quanh việc này nếu format
chỉ cần thực hiện một chuỗi thay vì yêu cầu các đối số riêng biệt, bằng cách tạo một chuỗi làm điều đúng. Dưới đây là một ví dụ nhỏ:
class DefaultList(list):
def __getitem__(self, idx):
try:
return super(DefaultList, self).__getitem__(idx)
except IndexError:
return '-'
Tất nhiên phiên bản đời thực của bạn sẽ quấn một iterable tùy ý, không phân lớp list
, và có lẽ sẽ phải sử dụng tee
hay một bộ nhớ cache nội bộ và kéo trong các giá trị mới theo yêu cầu, chỉ mặc định khi bạn đã kết thúc.(Bạn có thể muốn tìm kiếm công thức "lazy list" hoặc "lazy sequence" tại ActiveState, bởi vì có một vài trong số chúng làm điều này.) Nhưng điều này là đủ để hiển thị ví dụ.
Bây giờ, điều này giúp chúng tôi như thế nào? Nó không; *lst
trên một số DefaultList
sẽ chỉ cố gắng tạo một tuple trong số đó, cho chúng ta chính xác cùng một số đối số mà chúng tôi đã có. Nhưng nếu bạn có một phiên bản của format
mà chỉ có thể mất một chuỗi các args thay vào đó? Sau đó, bạn chỉ có thể vượt qua DefaultList
và nó sẽ hoạt động.
Và bạn có điều đó: Formatter.vformat
.
>>> string.Formatter().vformat('{0} {1} {2}', DefaultList([0, 1]), {})
'0 1 -'
Tuy nhiên, có một cách dễ dàng hơn, khi bạn đang sử dụng Formatter
rõ ràng thay vì ngầm bằng phương pháp str
. Bạn chỉ có thể ghi đè lên get_value
phương pháp của nó và/hoặc check_unused_args
của nó:
class DefaultFormatter(string.Formatter):
def __init__(self, default):
self.default = default
# Allow excess arguments
def check_unused_args(self, used_args, args, kwargs):
pass
# Fill in missing arguments
def get_value(self, key, args, kwargs):
try:
return super(DefaultFormatter, self).get_value(key, args, kwargs)
except IndexError:
return '-'
f = DefaultFormatter('-')
print(f.vformat('{0} {2}', [0], {}))
print(f.vformat('{0} {2}', [0, 1, 2, 3], {}))
Tất nhiên bạn vẫn sẽ cần phải quấn iterator của bạn trong một cái gì đó mà cung cấp giao thức Sequence.
Khi bạn ở đó, sự cố của bạn có thể được giải quyết trực tiếp hơn nếu ngôn ngữ có giao thức "giải nén có thể lặp lại". Xem here cho một chuỗi python-ideas đề xuất một thứ như vậy và tất cả các vấn đề mà ý tưởng có. (Cũng lưu ý rằng hàm format
sẽ làm cho điều này phức tạp hơn, bởi vì nó sẽ phải sử dụng giao thức giải nén trực tiếp thay vì dựa vào trình thông dịch để làm điều đó một cách kỳ diệu. Nhưng, giả sử nó làm như vậy, thì bạn chỉ cần viết một trình bao bọc đơn giản và đa năng xung quanh bất kỳ vòng lặp nào có thể xử lý __unpack__
cho nó.)
OK. Bạn có thể đề xuất một cách tiếp cận tốt hơn? Tôi không hài lòng với việc tạo ra một máy phát điện có độ dài tối đa, đó là lãng phí (đánh bại mục đích sử dụng một máy phát điện, một danh sách sẽ làm) và sẽ không được đảm bảo luôn hoạt động. – user443854
@ user443854: Bạn có thể sử dụng 'itertools.islice()' để giới hạn một trình tạo. –
Tôi biết 'itertools.islice()', nhưng tôi không thấy nó áp dụng ở đây như thế nào. Tôi cần phải biết số lượng các yếu tố cần thiết trước khi tôi có thể sử dụng nó. Tôi đã hy vọng đạt được điều gì đó khác biệt. Trong tiếng Anh đơn giản, tôi muốn nói với thông dịch viên: đây là một máy phát điện, lặp lại nó nhiều lần nếu cần, nhưng không nhiều hơn. – user443854