简体   繁体   中英

Python class variable accessed by multiple instances

I want to define a global variable which can be accessed (read and write) by all instances of the class. My current solution is shown in the example below. I don't like having a variable in the global namespace, but I was not able to put idx in class. How can I put idx in class and achieve the same?

# working
idx = 0
class test(object):
    def add(self):
        global idx
        idx += 1
    def print_x(self):
        global idx
        print(idx)

test1 = test()
test1.add()
test1.print_x()
test2 = test()
test2.add()
test2.print_x()

# Error
class test(object):
    idx = 0
    def add(self):
        global idx
        idx += 1
    def print_x(self):
        global idx
        print(idx)

test1 = test()
test1.add()
test1.print_x()
test2 = test()
test2.add()
test2.print_x()

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    test1.add()
  File "test.py", line 9, in add
    idx += 1
NameError: global name 'idx' is not defined

Your code fails because you tried to access a global variable idx without properly declaring it. You need to access your class variable.

class Test(object):
    idx = 0
    def add(self):
        Test.idx += 1

obj = Test()
obj.add()
print(Test.idx)
obj = Test()
obj.add()
print(Test.idx)

Output:

1
2

Here's a hacky little way that doesn't need any global variables. It makes use of the fact that default arguments are only created once when __init__ is called for the first time and if the default arguments are mutable, then changing one of them will impact all future functions/instances of the same class.

We can create idx as a list since lists are mutable and then make sure we only modify that list in place. Doing so like this will ensure that all instances of your Test class point to the exact same idx list. Changing one changes them all, so long as you only do in-place modifications.

class Test:
    def __init__(self, idx = [0]):
        self.idx = idx

    def add(self):
        self.idx[0] += 1

a = Test()
b = Test()

a.add()

print(a.idx, b.idx)
# >> Prints [1], [1]

You can achieve this using a singleton pattern. Here is a small example with singleton:

class Singleton:
    # Here will be the instance stored.
    __instance = None

    @staticmethod
    def getInstance():
        """ Static access method. """
        if Singleton.__instance == None:
            Singleton()
        return Singleton.__instance

    def add(self):
        self.idx += 1

    def __init__(self):
        """ Virtually private constructor. """
        if Singleton.__instance != None:
            raise Exception("This class is a singleton!")
        else:
            Singleton.__instance = self
            self.idx = 0
In [3]: main = Singleton()
In [4]: a = Singleton.getInstance()
In [5]: print(a.idx)
0

In [6]: a.add()
In [7]: print(a.idx)
1

In [8]: b = Singleton.getInstance()
In [9]: print(b.idx)
1

Ref: https://www.tutorialspoint.com/python_design_patterns/python_design_patterns_singleton.htm

There are some elegant Singleton examples on SO as well.

You have to define your variable in the class, outside of methods, and it shall not be a self variable. A self variable is unique to every instances. In methods you have to use the class name for accessing the variable, otherwise the method will look at it as a private variable, and in most cases you will get an error, because it was not initialized before usage.

class MyClass:
    my_public_variable = 0
    __my_private_variable = 5

    def inc(self):
        MyClass.my_public_variable += 1
        MyClass.__my_private_variable += 1

    def show(self):
        print(MyClass.my_public_variable)
        print(MyClass.__my_private_variable)

obj1 = MyClass()
obj1.show()

obj2 = MyClass()
obj2.show()

obj1.inc()
obj2.show()

print(obj1.my_public_variable)
print(obj1.my_private_variable) # Invokes error

By running this code, the following will be printed out without the parenthesis:

0  (my_public_variable of obj1)
5  (my_private_variable of obj1)
0  (my_public_variable of obj2)
5  (my_private_variable of obj2)
1  (my_public_variable of obj2, incremented by obj1)
6  (my_private_variable of obj2, incremented by obj1)
1  (my_public_variable of obj1, it can be accessed outside of the class, since it is a public variable of the class)
Error (because, as its name suggests, my_private_variable is a private variable of the class)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM