Tôi có một lớp chứa tùy chỉnh trong Python 2.7 và mọi thứ hoạt động như mong đợi ngoại trừ nếu tôi cố gắng mở rộng một thể hiện là **kwargs
cho một chức năng:Làm cho vùng chứa tùy chỉnh hoạt động với ** kwargs (cách Python mở rộng các arg?)
cm = ChainableMap({'a': 1})
cm['b'] = 2
assert cm == {'a': 1, 'b': 2} # Is fine
def check_kwargs(**kwargs):
assert kwargs == {'a': 1, 'b': 2}
check_kwargs(**cm) # Raises AssertionError
tôi đã ghi đè __getitem__
, __iter__
, iterkeys
, keys
, items
, và iteritems
, (và __eq__
và __repr__
) chưa ai trong số họ dường như được tham gia vào việc mở rộng như **kwargs
, những gì tôi làm sai rồi?
Edit - Các công tác nguồn cập nhật mà bây giờ được thừa hưởng từ MutableMapping và cho biết thêm các phương pháp còn thiếu:
from itertools import chain
from collections import MutableMapping
class ChainableMap(MutableMapping):
"""
A mapping object with a delegation chain similar to JS object prototypes::
>>> parent = {'a': 1}
>>> child = ChainableMap(parent)
>>> child.parent is parent
True
Failed lookups delegate up the chain to self.parent::
>>> 'a' in child
True
>>> child['a']
1
But modifications will only affect the child::
>>> child['b'] = 2
>>> child.keys()
['a', 'b']
>>> parent.keys()
['a']
>>> child['a'] = 10
>>> parent['a']
1
Changes in the parent are also reflected in the child::
>>> parent['c'] = 3
>>> sorted(child.keys())
['a', 'b', 'c']
>>> expect = {'a': 10, 'b': 2, 'c': 3}
>>> assert child == expect, "%s != %s" % (child, expect)
Unless the child is already masking out a certain key::
>>> del parent['a']
>>> parent.keys()
['c']
>>> assert child == expect, "%s != %s" % (child, expect)
However, this doesn't work::
>>> def print_sorted(**kwargs):
... for k in sorted(kwargs.keys()):
... print "%r=%r" % (k, kwargs[k])
>>> child['c'] == 3
True
>>> print_sorted(**child)
'a'=10
'b'=2
'c'=3
"""
__slots__ = ('_', 'parent')
def __init__(self, parent, **data):
self.parent = parent
self._ = data
def __getitem__(self, key):
try:
return self._[key]
except KeyError:
return self.parent[key]
def __iter__(self):
return self.iterkeys()
def __setitem__(self, key, val):
self._[key] = val
def __delitem__(self, key):
del self._[key]
def __len__(self):
return len(self.keys())
def keys(self, own=False):
return list(self.iterkeys(own))
def items(self, own=False):
return list(self.iteritems(own))
def iterkeys(self, own=False):
if own:
for k in self._.iterkeys():
yield k
return
yielded = set([])
for k in chain(self.parent.iterkeys(), self._.iterkeys()):
if k in yielded:
continue
yield k
yielded.add(k)
def iteritems(self, own=False):
for k in self.iterkeys(own):
yield k, self[k]
def __eq__(self, other):
return sorted(self.iteritems()) == sorted(other.iteritems())
def __repr__(self):
return dict(self.iteritems()).__repr__()
def __contains__(self, key):
return key in self._ or key in self.parent
def containing(self, key):
"""
Return the ancestor that directly contains ``key``
>>> p2 = {'a', 2}
>>> p1 = ChainableMap(p2)
>>> c = ChainableMap(p1)
>>> c.containing('a') is p2
True
"""
if key in self._:
return self
elif hasattr(self.parent, 'containing'):
return self.parent.containing(key)
elif key in self.parent:
return self.parent
def get(self, key, default=None):
"""
>>> c = ChainableMap({'a': 1})
>>> c.get('a')
1
>>> c.get('b', 'default')
'default'
"""
if key in self:
return self[key]
else:
return default
def pushdown(self, top):
"""
Pushes a new mapping onto the top of the delegation chain:
>>> parent = {'a': 10}
>>> child = ChainableMap(parent)
>>> top = {'a': 'apple', 'b': 'beer', 'c': 'cheese'}
>>> child.pushdown(top)
>>> assert child == top
This creates a new ChainableMap with the contents of ``child`` and makes it
the new parent (the old parent becomes the grandparent):
>>> child.parent.parent is parent
True
>>> del child['a']
>>> child['a'] == 10
True
"""
old = ChainableMap(self.parent)
for k, v in self.items(True):
old[k] = v
del self[k]
self.parent = old
for k, v in top.iteritems():
self[k] = v
Thử bước qua một trình gỡ rối (hoặc viết các câu lệnh 'in' trong mọi hàm quá tải) để xem hàm nào đang được gọi khi mở rộng đối số. – Lanaru
Lưu ý rằng ngay cả khi thao tác này hoạt động, 'check_args' sẽ nhận được từ điển * mới *, không phải lớp con của bạn. Xem [tài liệu định nghĩa hàm] (http://docs.python.org/reference/compound_stmts.html#function-definitions); cụ thể * "Nếu biểu mẫu" ** số nhận dạng "hiện diện, nó được khởi tạo từ điển mới nhận bất kỳ đối số từ khóa dư thừa nào, mặc định là từ điển trống mới." *. –
@Lanaru đặt một 'pdb nhập; pdb.set_trace() 'ngay trước lệnh gọi' check_kwargs' và thực hiện một bước duy nhất đặt tôi qua điểm arg được mở rộng. Đặt cùng một 'set_trace' trong mỗi hàm ghi đè cho thấy không có hàm nào trong số chúng được gọi. – grncdr