Rất nhiều câu trả lời có rất ít thông tin đầy đủ, vì vậy tôi muốn tổng hợp tất cả lại với (các) mẫu yêu thích của mình.
giá trị mặc định là một mutable
loại
Nếu giá trị mặc định là một đối tượng có thể thay đổi, bạn thật may mắn: bạn có thể khai thác thực tế là các đối số mặc định của Python được đánh giá một lần khi hàm được xác định (một số thông tin thêm về điều này ở cuối câu trả lời trong phần cuối cùng)
Điều này có nghĩa là bạn có thể dễ dàng so sánh một giá trị có thể thay đổi mặc định bằng cách sử dụng is
để xem liệu nó được chuyển dưới dạng đối số hay được để mặc định, như trong các ví dụ sau dưới dạng hàm hoặc phương thức:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
và
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Đối số mặc định bất biến
Bây giờ, sẽ kém thanh lịch hơn một chút nếu mặc định của bạn được mong đợi là một immutable
giá trị (và hãy nhớ rằng ngay cả các chuỗi cũng không thay đổi!) Bởi vì bạn không thể khai thác thủ thuật như nó vốn có, nhưng vẫn có điều bạn có thể làm, vẫn khai thác có thể thay đổi kiểu; về cơ bản, bạn đặt mặc định "giả" có thể thay đổi trong chữ ký hàm và giá trị mặc định "thực" mong muốn trong thân hàm.
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
print(value)
Cảm giác đặc biệt buồn cười nếu mặc định thực của bạn là None
, nhưng None
không thay đổi được, vì vậy ... bạn vẫn cần sử dụng rõ ràng một biến có thể thay đổi làm tham số mặc định của hàm và chuyển sang Không có trong mã.
Sử dụng một Default
lớp cho các giá trị mặc định không thể thay đổi
hoặc, tương tự như đề xuất @cz, nếu tài liệu python không đủ :-), bạn có thể thêm một đối tượng vào giữa để làm cho API rõ ràng hơn (mà không cần đọc tài liệu); cá thể lớp used_proxy_ Default có thể thay đổi được và sẽ chứa giá trị mặc định thực mà bạn muốn sử dụng.
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
hiện nay:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Các công trình trên cũng độc đáo cho Default(None)
.
Các mẫu khác
Rõ ràng là các mẫu ở trên trông xấu hơn chúng nên làm vì tất cả những mẫu print
đó chỉ để hiển thị cách chúng hoạt động. Nếu không, tôi thấy chúng ngắn gọn và đủ lặp lại.
Bạn có thể viết trình trang trí để thêm __call__
mẫu do @dmg đề xuất theo cách hợp lý hơn, nhưng điều này vẫn bắt buộc phải sử dụng các thủ thuật kỳ lạ trong chính định nghĩa hàm - bạn sẽ cần phải tách ra value
và value_default
nếu mã của bạn cần phân biệt chúng, vì vậy Tôi không thấy nhiều lợi thế và tôi sẽ không viết ví dụ :-)
Các loại có thể thay đổi làm giá trị mặc định trong Python
Thông tin thêm về # 1 python gotcha! , bị lạm dụng vì niềm vui của riêng bạn ở trên. Bạn có thể thấy những gì xảy ra do đánh giá ở định nghĩa bằng cách thực hiện:
def testme(default=[]):
print(id(default))
Bạn có thể chạy testme()
bao nhiêu lần tùy thích, bạn sẽ luôn thấy một tham chiếu đến cùng một trường hợp mặc định (vì vậy về cơ bản mặc định của bạn là không thay đổi :-)).
Hãy nhớ rằng trong Python chỉ có 3 có thể thay đổi được xây dựng trong các loại : set
, list
, dict
; mọi thứ khác - ngay cả chuỗi! - là bất biến.