Có cách nào để phân lớp từ dict và bộ sưu tập.abc.MutableMapping cùng nhau không?


26

Ví dụ, giả sử tôi muốn phân lớp dictvà có tất cả các khóa được viết hoa:

class capdict(dict):
    def __init__(self,*args,**kwds):
        super().__init__(*args,**kwds)
        mod = [(k.capitalize(),v) for k,v in super().items()]
        super().clear()
        super().update(mod)
    def __getitem__(self,key):
        return super().__getitem__(key.capitalize())
    def __setitem__(self,key,value):
        super().__setitem__(key.capitalize(),value)
    def __delitem__(self,key):
        super().__detitem__(key.capitalize())

Điều này hoạt động đến một mức độ,

>>> ex = capdict(map(reversed,enumerate("abc")))
>>> ex
{'A': 0, 'B': 1, 'C': 2}
>>> ex['a']
0

nhưng, tất nhiên, chỉ cho các phương pháp tôi nhớ để thực hiện, ví dụ

>>> 'a' in ex
False

không phải là hành vi mong muốn.

Bây giờ, cách lười biếng điền vào tất cả các phương thức có thể được bắt nguồn từ những cái "cốt lõi" sẽ được trộn lẫn vào collections.abc.MutableMapping. Chỉ, nó không hoạt động ở đây. Tôi đoán bởi vì các phương thức trong câu hỏi ( __contains__trong ví dụ) đã được cung cấp bởi dict.

Có cách nào để có bánh của tôi và ăn nó? Một số phép thuật MutableMappingchỉ cho phép xem các phương thức mà tôi đã ghi đè để nó giới thiệu lại các phương thức khác dựa trên những phương pháp đó?


Bạn có thể không cần sử dụng MutableMapping. Xem trường hợp từ điển không nhạy cảm .
martineau

@martineau cảm ơn, như tôi đã nói đó chỉ là một ví dụ.
Paul Panzer

Bạn có thể sử dụng os._Environ.
Peter Wood

Câu trả lời:


26

Bạn có thể làm gì:

Điều này có thể sẽ không hoạt động tốt (nghĩa là không phải là thiết kế sạch nhất), nhưng bạn có thể thừa hưởng từ MutableMapping trước và sau đó từ dict second.

Sau đó MutableMapping sẽ sử dụng bất kỳ phương thức nào bạn đã triển khai (vì chúng là phương thức đầu tiên trong chuỗi tra cứu):

>>> class D(MutableMapping, dict):
        def __getitem__(self, key):
            print(f'Intercepted a lookup for {key!r}')
            return dict.__getitem__(self, key)


>>> d = D(x=10, y=20)
>>> d.get('x', 0)
Intercepted a lookup for 'x'
10
>>> d.get('z', 0)
Intercepted a lookup for 'z'
0

Cách tốt hơn:

Cách tiếp cận sạch nhất (dễ hiểu và kiểm tra) là chỉ kế thừa từ MutableMapping và sau đó thực hiện các phương thức cần thiết bằng cách sử dụng một lệnh chính quy làm kho lưu trữ dữ liệu cơ sở (với thành phần thay vì kế thừa):

>>> class CapitalizingDict(MutableMapping):
        def __init__(self, *args, **kwds):
            self.store = {}
            self.update(*args, **kwds)
        def __getitem__(self, key):
            key = key.capitalize()
            return self.store[key]
        def __setitem__(self, key, value):
            key = key.capitalize()
            self.store[key] = value
        def __delitem__(self, key):
            del self.store[key]
        def __len__(self):
            return len(self.store)
        def __iter__(self):
            return iter(self.store)
        def __repr__(self):
            return repr(self.store)


>>> d = CapitalizingDict(x=10, y=20)
>>> d
{'X': 10, 'Y': 20}
>>> d['x']
10
>>> d.get('x', 0)
10
>>> d.get('z', 0)
0
>>> d['w'] = 30
>>> d['W']
30

Cảm ơn! Tôi có thể đã thề rằng tôi đã thử cả hai đơn hàng ... Không quan tâm, khi tôi sử dụng phương thức "có thể làm", hoán đổi tất cả các supers cho dictcác s rõ ràng thì nó dường như hoạt động, ngoại trừ lentrả về 0. Nó đến từ đâu vậy?
Paul Panzer

2
Cuộc gọi super () từ __len _ () __ đi đến cuộc gọi tiếp theo trong mro : (D, MutableMapping, dict). Đó là phương thức MutableMappiing .__ len __ () luôn trả về 0. Nó không được gọi trực tiếp - nó luôn được cho là bị ghi đè. Đó là lý do tại sao bạn phải gọi dict.__len__(self)trực tiếp. Và đó là một trong những lý do mà tôi đã nói "điều này có thể sẽ không hoạt động tốt" ;-)
Raymond Hettinger
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.