开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜