Có ít nhất năm sáu cách. Cách ưa thích phụ thuộc vào trường hợp sử dụng của bạn là gì.
Tùy chọn 1: Chỉ cần thêm một asdict()phương thức.
Dựa trên mô tả vấn đề, tôi rất muốn xem xét asdictcách làm việc được đề xuất bởi các câu trả lời khác. Điều này là do có vẻ như đối tượng của bạn không thực sự là một bộ sưu tập:
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
Việc sử dụng các tùy chọn khác bên dưới có thể gây nhầm lẫn cho những người khác trừ khi rất rõ ràng chính xác thành viên đối tượng nào sẽ và sẽ không được lặp lại hoặc được chỉ định dưới dạng cặp khóa-giá trị.
Tùy chọn 1a: Kế thừa lớp của bạn từ 'typing.NamedTuple'(hoặc phần lớn là tương đương 'collections.namedtuple') và sử dụng _asdictphương pháp được cung cấp cho bạn.
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
Sử dụng một tuple được đặt tên là một cách rất tiện lợi để thêm nhiều chức năng vào lớp của bạn mà không tốn nhiều công sức, bao gồm cả một _asdictphương thức . Tuy nhiên, một hạn chế là, như đã trình bày ở trên, NT sẽ bao gồm tất cả các thành viên trong nó _asdict.
Nếu có những thành viên bạn không muốn đưa vào từ điển của mình, bạn sẽ cần sửa đổi _asdictkết quả:
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
def _asdict(self):
d = super()._asdict()
del d['sum']
del d['version']
return d
Một hạn chế khác là NT là chỉ đọc. Điều này có thể mong muốn hoặc không.
Phương án 2: Thực hiện __iter__.
Ví dụ như thế này:
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
Bây giờ bạn chỉ có thể làm:
dict(my_object)
Điều này hoạt động vì hàm dict()tạo chấp nhận một (key, value)cặp có thể lặp lại để tạo từ điển. Trước khi thực hiện việc này, hãy tự đặt câu hỏi liệu việc lặp lại đối tượng dưới dạng một chuỗi các cặp khóa, giá trị theo cách này - trong khi thuận tiện cho việc tạo dict- có thực sự là một hành vi đáng ngạc nhiên trong các ngữ cảnh khác hay không. Ví dụ, hãy tự đặt câu hỏi "hành vi list(my_object)nên là ...?"
Ngoài ra, hãy lưu ý rằng việc truy cập trực tiếp các giá trị bằng obj["a"]cú pháp get item sẽ không hoạt động và việc giải nén đối số từ khóa sẽ không hoạt động. Đối với những thứ đó, bạn cần triển khai giao thức ánh xạ.
Lựa chọn 3: Thực hiện các giao thức lập bản đồ . Điều này cho phép hành vi truy cập theo từng khóa, truyền đến một dictmà không cần sử dụng __iter__và cũng cung cấp hành vi giải nén ( {**my_obj}) và hành vi giải nén từ khóa nếu tất cả các khóa đều là chuỗi ( dict(**my_obj)).
Giao thức ánh xạ yêu cầu bạn cung cấp (tối thiểu) hai phương pháp cùng nhau: keys()và __getitem__.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
Bây giờ bạn có thể làm những việc như:
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m)
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m)
{'a': 'one', 'b': 'two', 'c': 'three'}
Như đã đề cập ở trên, nếu bạn đang sử dụng phiên bản python đủ mới, bạn cũng có thể giải nén đối tượng giao thức ánh xạ của mình thành một dạng từ điển như vậy (và trong trường hợp này, khóa của bạn không bắt buộc phải là chuỗi):
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
Lưu ý rằng các giao thức lập bản đồ được ưu tiên hơn các __iter__phương pháp khi đúc một đối tượng đến một dictcách trực tiếp (mà không sử dụng kwarg giải nén, ví dụ dict(m)). Vì vậy, có thể- và đôi khi thuận tiện- khiến đối tượng có hành vi khác khi được sử dụng như một đối tượng có thể lặp lại (ví dụ list(m):) so với khi ép kiểu thành một dict( dict(m)).
NHÂN SỰ : Chỉ vì bạn CÓ THỂ sử dụng giao thức ánh xạ, KHÔNG có nghĩa là bạn NÊN làm như vậy . Liệu nó thực sự có ý nghĩa cho đối tượng của bạn để được thông qua xung quanh như một tập hợp các cặp khóa-giá trị, hoặc như các đối số và giá trị từ khóa? Việc truy cập nó bằng phím- giống như từ điển- có thực sự hợp lý không?
Nếu câu trả lời cho những câu hỏi này là có , có lẽ bạn nên xem xét lựa chọn tiếp theo.
Tùy chọn 4: Xem xét sử dụng 'collections.abcmô-đun ' .
Kế thừa lớp của bạn từ 'collections.abc.Mappinghoặc 'collections.abc.MutableMappingbáo hiệu cho những người dùng khác rằng, đối với mọi ý định và mục đích, lớp của bạn là một ánh xạ * và có thể được mong đợi sẽ hoạt động theo cách đó.
Bạn vẫn có thể truyền đối tượng của mình đến một đối tượng dictnhư bạn yêu cầu, nhưng có lẽ sẽ có rất ít lý do để làm như vậy. Do tính năng gõ phím , nên việc bận tâm truyền đối tượng ánh xạ của bạn sang a dictsẽ chỉ là một bước bổ sung không cần thiết trong phần lớn thời gian.
Câu trả lời này cũng có thể hữu ích.
Như đã lưu ý trong các nhận xét bên dưới: điều đáng nói là làm điều này theo cách abc về cơ bản biến lớp đối tượng của bạn thành dictlớp giống như (giả sử bạn sử dụng MutableMappingchứ không phải Mappinglớp cơ sở chỉ đọc ). Mọi thứ bạn có thể làm dict, bạn có thể làm với đối tượng lớp của riêng bạn. Điều này có thể được, hoặc có thể không, mong muốn.
Ngoài ra, hãy xem xét abcs số trong numbersmô-đun:
https://docs.python.org/3/library/numbers.html
Vì bạn cũng đang truyền đối tượng của mình tới một int, có thể có ý nghĩa hơn khi chuyển về cơ bản lớp của bạn thành một lớp hoàn chỉnh intđể việc truyền không cần thiết.
Tùy chọn 5: Xem xét việc sử dụng dataclassesmô-đun (chỉ Python 3.7), bao gồm một asdict()phương thức tiện ích thuận tiện.
from dataclasses import dataclass, asdict, field, InitVar
@dataclass
class Wharrgarbl(object):
a: int
b: int
c: int
sum: InitVar[int]
version: InitVar[str] = "old"
def __post_init__(self, sum, version):
self.sum = 6
self.version = str(version)
Bây giờ bạn có thể làm điều này:
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
Tùy chọn 6: Sử dụng typing.TypedDict, đã được thêm vào python 3.8 .
LƯU Ý: lựa chọn 6 có thể KHÔNG phải là điều mà OP, hoặc những độc giả khác dựa trên tiêu đề của câu hỏi này, đang tìm kiếm. Xem thêm bình luận bên dưới.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
Sử dụng tùy chọn này, đối tượng kết quả là adict (nhấn mạnh: nó không phải là a Wharrgarbl). Không có lý do gì để "truyền" nó thành một chính tả (trừ khi bạn đang tạo một bản sao).
Và vì đối tượng là adict , chữ ký khởi tạo giống với chữ ký của dictvà như vậy nó chỉ chấp nhận các đối số từ khóa hoặc một từ điển khác.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
Nhấn mạnh : "lớp" ở trên Wharrgarblthực ra không phải là một lớp mới. Nó chỉ đơn giản là đường cú pháp để tạo các dictđối tượng được định kiểu với các trường thuộc các kiểu khác nhau cho trình kiểm tra kiểu.
Do đó, tùy chọn này có thể khá thuận tiện để báo hiệu cho người đọc mã của bạn (và cả cho một trình kiểm tra kiểu như mypy) rằng một dictđối tượng như vậy được mong đợi có các khóa cụ thể với các kiểu giá trị cụ thể.
Nhưng điều này có nghĩa là bạn không thể, ví dụ, thêm các phương pháp khác, mặc dù bạn có thể thử:
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
... nhưng nó sẽ không hoạt động:
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
* "Lập bản đồ" đã trở thành "tên" tiêu chuẩn của dictloại vịt giống