问题
People tend to consider getters and setters un-Pythonic, prefering to use @property
instead. I'm currently trying to extend the functionality of a class that uses @property
to support a dict:
class OldMyClass(object):
@property
def foo(self):
return self._foo
@foo.setter
def foo(self, value):
self.side_effect(value)
self._foo = value
class NewMyClass(object):
@property
def foo(self, key): # Invalid Python
return self._foo[key]
@foo.setter
def foo(self, key, value): # Invalid Python
self.side_effect(key, value)
self._foo[key] = value
Of course, I could create some helper classes to deal with this, but it seems like just writing setter/getter functions that take a key and value would be much simpler. But is there a more Pythonic way to do this refactor?
回答1:
What you describe is not a case for properties. Properties are for allowing obj
(or rather, its class) to customize the behavior of obj.prop = value
. If you want to customize obj.prop[key] = value
, then you need to handle that not in obj
but in obj.prop
, which means you need to make obj.prop
be a custom class. What you want here is to create a custom class with a __setitem__
.
class Foo(object):
def __init__(self):
self._vals = {}
def __setitem__(self, key, val):
do_side_effect(key, val)
self._vals[key] = val
Then in your MyClass's __init__
, do self.foo = Foo()
.
If you want this dict-like object to know what its parent object is (e.g., because the side effect should take place on the parent, then pass the parent as an argument to Foo:
class Foo(object):
def __init__(self, parent):
self.parent = parent
self._vals = {}
def __setitem__(self, key, val):
parent.do_side_effect(key, val)
self._vals[key] = val
And then in MyClass.__init__
you do self.foo = Foo(self)
.
With regard to getters/setters, what you're doing is not a typical getter/setter. The kind of thing that is discouraged is things like getSomeProp()
and setSomeProp(value)
, where there is a separate method for each property that you get/set. In your example, you're describing something different, which is a generalized get/set mechanism for setting key-value pairs (which presumably have some meaning for the object's behavior). Having a method setPair(key, value)
is not any worse than having a method called doSomething(arg1, arg2)
. Here you have an extra parameter, so your task doesn't fit into the simple pattern of obj.key = value
. You also have an extra layer of indirection if you want the key/value inobj.prop[key] = value
to "know about" obj
; in an assignment like obj.prop[key] = value
, the key and value are one step removed from obj
and interact directly only with prop
.
You could also do it with a simple method as John Smith Optional suggests. Whether you want to do it that way or with a custom object like I described depends on how you want the API set up. There can be readability benefits to having the code built around custom "data-holding" classes like the one I outlined here, but it does make the implementation a bit more complex.
来源:https://stackoverflow.com/questions/22900940/pythonic-alternative-to-dict-style-setter