How do I use a static variable inside a class in Python - python

class Cls:
counter = 0
def __init__(self, name):
self.name = name
self.counter += 1
def count(self):
return self.counter
I'm learning python, what I want to have is a static counter that counts the number of times the class has been instantiated, but every time I create an instance counter gets recreated and count() function always returns 1.
I want something that in java would look like this
public class Cls {
private static int counter = 0;
private String name;
public Cls(String name) {
this.name = name;
counter ++;
}
public static int count(){
return counter;
}
}

There are two ways to access a class attribute: you can either access it directly on a class, or you can read it through self (but not rebind it). Accessing a class attribute through self won't work if there is already a value set directly on the instance so you would normally try to use the class to access a class attribute.
class Cls:
counter = 0
def __init__(self, name):
self.name = name
Cls.counter += 1
def count(self):
return Cls.counter
When you write self.counter += 1 this is a shorthand for self.counter = self.counter + 1 and as with any other binding through self it sets an instance attribute.
This can be useful if you want a default value for instance attributes, you can set them as class attributes and then just update them in the instances which want different values, but to avoid confusion you probably want to avoid using self at all when accessing class attributes.
You can also consider making the count method into a class method and moving the increment into another method:
#classmethod
def increment(cls):
cls.counter += 1
#classmethod
def count(cls):
return cls.counter
if you do that then each subclass will have its own independent counter. That may or may not be what you want here. The cls parameter here is the class that was actually instantiated, this can be useful if you can a whole class hierarchy, or even just a base class CountsInstances where you can put this code once and reuse it with multiple independent counters.
Decorating each function with #staticmethod will give you something close to the Java code:
class Cls:
counter = 0
def __init__(self, name):
self.name = name
self.increment()
#staticmethod
def increment():
Cls.counter += 1
#staticmethod
def count():
return Cls.counter

Do not use Cls.
class MyClass:
counter = 0
def __init__(self, name):
self.name = name
self.counter += 1 # this creates an instance variable counter
# thats initialized by counter if you do not set it
# it is NOT shared between instances, but specific to each
Instead you should increment the static variable:
def __init__(self, name):
self.name = name
MyClass.counter += 1 # this increments the static class variable
If you fix
#staticmethod
def count():
return MyClass.counter
this way, you can still call count() on instances as well as directly on the class.
t = MyClass("some")
print( MyClass.count() ) # fine
t1 = MyClass("other")
print( t.count() ) # only allowed if prefix the method with #staticmethod
Output:
1
2
See What is the difference between #staticmethod and #classmethod in Python? for further infos.

Related

Accesing superclass attribute during subclass construction in Python

Is it possible to access superclass's attributes during class construction?
Here's my code:
class A:
x = 1
class B(A):
x += 1 # <- error
The increment line x += ... is not valid, because x is not defined at this moment. You may suggest doing it in __init__, but I need to initialize the values before the constructor and want to minimize the code.
The code x = A.x + 1 will not work in my case because the A is generated in run-time. So the real code for B looks like
class A:
x: int
def class_gen(params):
class SubA(A):
x = 1
return SubA
class B(class_gen(some_params)):
x += 1
I've found a weird workaround:
x = A.__subclasses__()[-1].x + 1 (indeed the last subclass of A will be the generated super), but it looks too dirty and unstable.
Another workaround is declaring the a 'stub' class variable:
...
_Stub = class_gen(some_params)
class B(_Stub):
x = _Stub.x + 1
but is it also looks urgly. Is there a better way for a perfectionist?
I think you can accomplish it by using __new__ in the subclass.
class A:
x = 1
class B(A):
def __new__(cls, *args, **kwargs):
cls.x += 1
return super().__new__(cls, *args, **kwargs)
b = B()
print(b.x) # 2
There's no easy way to do this. Probably the best is to give your B class a metaclass that handles incrementing of the x attribute for you:
class XIncrementerMeta(type):
def __new__(mcls, name, bases, namespace):
namespace['x'] = bases[0].x + 1
return super().__new__(mcls, name, bases, namespace)
class B(class_gen("some_params"), metaclass=XIncrementerMeta):
pass
print(B.x) # prints 2
If you need each B-like class to have its own kind of manipulation of the x value, you could put it in a classmethod and have an inherited metaclass call it. Or you could even have an __init_subclass__ method in A that calls methods to set the value on its subclasses:
class A:
def __init_subclass__(cls):
cls.set_X()
class SubA(A):
#classmethod
def set_X(cls):
cls.x = 1
class B(SubA):
#classmethod
def set_X(cls):
super().set_X() # get SubA to set an initial value
cls.x += 1 # then update it
print(B.x) # prints 2

How to read the name of the variable at initialization and store it n a descriptor class in python?

My plan is this: Using python descriptors, I want to read the name of the variable at assignment time.
Current code:
class Decriptor:
def __init__(self, field_type: typing.Type[typing.Any]):
self.field_type = field_type
self.value = None
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, constant):
if issubclass(type(constant), self.field_type):
self.value = constant
else:
raise TypeError(f"expected an instance of type {self.field_type.__name__} for attribute {}, got {type(constant).__name__} instead")
My reason for using descriptors is to implement static typing at compile time and to provide a wrapper for field assignment and declaration.
If I were to implement this in a class...
class Foo:
num = Decriptor(field_type=int)
def __init__(self, num):
self.z = num
Objective: How do I take the name of the variable num as a string without direct reference and assign it to a field in the Descriptor class, say self.name? So when I change the values in different instances of the class, I can just write instance.__dict__[self.name] = constant?
Since Python 3.7, we have method __set_name__, called at initialization of descriptor that reads the name of the variable
#add this to code
def __set_name__(self, owner, name):
self.name = name

How to restrict object creation

Consider following example
class Key:
def __init__(self, s):
self.s = s
d = {}
for x in range(1, 10000):
t = Key(x)
d[t] = x
This will create 10000 keys. Is it possible to control the object creation of class key, for example we cannot create more than 5 objects of key class. The loop should not be changed in any ways.
You can control how, or how many objects are created by giving your class a __new__ method:
class Key(object):
_count = 0
def __new__(cls, s):
if cls._count == 5:
raise TypeError('Too many keys created')
cls._count += 1
return super(Key, cls).__new__(cls, s)
def __init__(self,s):
self.s = s
Key.__new__() is called to create a new instance; here I keep a count of how many are created, and if there are too many, an exception is raised. You could also keep a pool of instances in a dictionary, or control creating of new instance in other ways.
Note that this only works for new-style classes, inheriting from object.
You can also use a metaclass approach
import weakref
import random
class FiveElementType(type):
def __init__(self, name, bases, d):
super(FiveElementType, self).__init__(name, bases, d)
self.__cache = weakref.WeakValueDictionary()
def __call__(self, *args):
if len(self.__cache) == 5:
return random.choice(self.__cache.values())
else:
obj = super(FiveElementType, self).__call__(*args)
self.__cache[len(self.__cache)] = obj
return obj
class Key(object):
__metaclass__ = FiveElementType
def __init__(self, s):
self.s = s
You can choose a random element or select it on the base of stored index. In this approach your loop don't fail with an exception that can be right or not, depending on your intention.

Python: How to define a variable in an __init__ function with a class method?

class TextToNumbers():
def __init__(self, number):
self.text = str(number)
self.chunks = parse_text_to_chunks(self.text)
def parse_text_to_chunks(text_to_parse):
#stuff
This is an example of a class I'm building. I want it to define some variables with class methods on initialization. (at least I think that is what I want) I think if I talk about my end goal it is that I have a set of complex information that needs to be available about a class directly at initialization. How do I call a class method at initialization, to define a class variable?
If you are looking for a way to call an instance method during initialization, you can use self to call that like this
class TextToNumbers():
def __init__(self, number):
self.text = str(number)
self.chunks = self.parse_text_to_chunks(self.text)
print self.chunks
def parse_text_to_chunks(self, text_to_parse):
# 1st parameter passed is the current INSTANCE on which this method is called
self.var1 = text_to_parse[1:]
return self.var1
TextToNumbers(123)
And I believe this is what you really need. But if you want a class method
class TextToNumbers():
def __init__(self, number):
self.text = str(number)
self.chunks = TextToNumbers.parse_text_to_chunks(self.text)
print self.chunks
#classmethod
def parse_text_to_chunks(cls, text_to_parse):
# 1st parameter passed is the current CLASS on which this method is called
cls.var1 = text_to_parse[1:]
return cls.var1
TextToNumbers(123)
But there is no point in creating a class method to initialize a class variable in __init__, since a class variable is shared by all the instances of the class, calling from __init__ will overwrite everytime an object is created.

Why I have 2 after second call?

class s:
i = []
def inc():
t = s()
t.i.append(len(t.i))
return len(t.i)
print(inc())
print(inc())
my output:
1
2
but I expected:
1
1
becouse everytime created new object, where my mistake?
You are appending to a variable of the class, not a variable of the instance
class s:
i = []
This code creates a variable in the class. This is similar to the concept of a static variable in Java or C++.
In java:
class S {
static List i = new ...
}
You probably wanted to do this:
class s:
def __init__(self):
self.i = []
This creates a variable in the instance, which is named self (similar to this in Java or C++). __init__ is the constructor of the class.
You should make i instance variable
class s:
def __init__(self):
self.i = []
You have made i a variable of the class itself.
If you want a variable for each class instance use self
class s:
def __init__(self):
self.i = []
You need to modify the instance variable:
class s:
def __init__(self):
self.i = []
def inc():
t = s()
t.i.append(len(t.i))
return len(t.i)
You are now modifying the same list of class s and hence it increments from 1 to 2.
Try making i an instance variable.
class s:
def __init__(self):
self.i = []

Categories