Python Reference to Member Variable Which is Immutable
I'm having a problem with not being able to reference a member variable indirectly.
I have a class with a function which does one thing. This function uses a member variable during its execution, and which member variable it uses is dependent on the input parameter cmd. Here's some code:
class Foo(object):
def __init__(self):
self.a = 0
self.b = 0
def bar(self, cmd):
if cmd == "do_this":
index = self.a
elif cmd == "do_that":
index = self.b
filename = "file-%d.txt" % index
fp = open("filename", "w")
# do some more things which depend on and *should* change
# the value of开发者_运维知识库 self.a or self.b
return
Now I understand why this code doesn't do what I want. Because we are using the immutable int type, our variable index refers to the value of the member variable self.a or self.b, not self.a or self.b themselves. By the end of the function, index refers to a different valued integer, but self.a and self.b are unchanged.
I'm obviously thinking too C++-ish. I want something equivalent to
index = &(self.a)
filename = "file-%d.txt" % *index
How do I accomplish this in a Pythonic way?
There are any number of ways to do this--setattr
and the attribute name as a string, writing functions or methods which do the setting for each attribute, etc. One that is fairly clear and may be convenient is
class Foo(object):
def __init__(self):
self.values = {'a': 0, 'b': 0}
def bar(self, cmd):
if cmd == "do_this":
index = 'a'
elif cmd == "do_that":
index = 'b'
filename = "file-%d.txt" % index
f = open("filename", "w")
...
self.values[index] = something
Your intuition to avoid using attribute names as strings is usually right (although that is easily as good a solution in this particular case), and the stock solution to avoiding looking up attributes and variables by strings is "use a dict".
In this particular case, I would simply keep track of the string name of the attribute you're interested in. something like:
class Foo(object):
def __init__(self):
self.a = 0
self.b = 0
def bar(self, cmd):
if cmd == "do_this":
index_name = 'a'
elif cmd == "do_that":
index_name = 'b'
index = getattr(self, index_name)
filename = "file-%d.txt" % index
fp = open("filename", "w")
# do some more things which depend on and *should* change
# the value of index
# ^^^^^
setattr(self, index_name, index)
return
Obviously if you're having a problem because the value is immutable, make it mutable! This of course is a complete hack and probably doesn't meet any definition of "Pythonic".
class Foo(object):
def __init__(self):
self.a = [0]
self.b = [0]
def bar(self, cmd):
if cmd == "do_this":
index = self.a
elif cmd == "do_that":
index = self.b
filename = "file-%d.txt" % index[0]
fp = open("filename", "w")
# do some more things which depend on and *should* change
# the value of self.a or self.b
index[0] = 42
return
If you don't want to keep track of strings, you can abstract that away using an attribute proxy class like the following:
class AttributeProxy(object):
def __init__(self, obj_inst, attr_name):
self.obj_inst, self.attr_name = obj_inst, attr_name
@property
def value(self):
return getattr(self.obj_inst, self.attr_name)
@value.setter
def value(self, value):
setattr(self.obj_inst, self.attr_name, value)
Usage:
class Test(object):
x = 0
y = 1
test = Test()
x = AttributeProxy(test, "x")
y = AttributeProxy(test, "y")
print x.value # 0
x.value += 1
print x.value == y.value
Of course, it's still using getattr()
and setattr()
behind the scenes, but the client code looks a little nicer. Compare x.value += 1
to setattr(obj, "x", getattr(obj, "x") + 1)
frex.
精彩评论