Apply a function to all instances of a class - python

I am looking for a way to apply a function to all instances of a class. An example:
class my_class:
def __init__(self, number):
self.my_value = number
self.double = number * 2
#staticmethod
def crunch_all():
# pseudocode starts here
for instances in my_class:
instance.new_value = instance.my_value + 1
So the command my_class.crunch_all() should add a new attribute new_value to all existing instances. I am guessing I will have to use #staticmethod to make it a "global" function.
I know I could keep track of the instances that are being defined by adding something like my_class.instances.append(number) in __init__ and then loop through my_class.instances, but I had no luck so far with that either. Also I am wondering if something more generic exists. Is this even possible?

Register objects with the class at initialisation (i.e. __init__) and define a class method (i.e. #classmethod) for the class:
class Foo(object):
objs = [] # registrar
def __init__(self, num):
# register the new object with the class
Foo.objs.append(self)
self.my_value = num
#classmethod
def crunch_all(cls):
for obj in cls.objs:
obj.new_value = obj.my_value + 1
example:
>>> a, b = Foo(5), Foo(7)
>>> Foo.crunch_all()
>>> a.new_value
6
>>> b.new_value
8

Related

Is there a way to pass a function call to an inner object?

Is there a way in python to pass a function call to an inner object, maybe through a decorator or wrapper? In the example below, class A holds a list of class B objects, and one of the class B objects is selected as the active object. I want class A to function as a passthrough, just identifying which of the class B objects that the call goes to. However, class A doesn't know what type of class it is going to hold beforehand, so I can't just add a set_var function to class A. It has to work for any generic function that class B has. It will only have one type of class in its objects list, so it could take class B as an input when it is instantiated and dynamically create functions, if that's a possibility. The client wouldn't know whether it's dealing with class A or class B. The code below is as far as I got.
class A:
def __init__(self):
self.objects = []
self.current_object = 0
def add_object(self, object):
self.objects.append(object)
class B:
def __init__(self):
self.var = 10
def set_var(self, new_var):
self.var = new_var
a_obj = A()
b_obj1 = B()
b_obj2 = B()
a_obj.add_object(b_obj1)
a_obj.add_object(b_obj2)
a_obj.set_var(100)
You could use the generic __getattr__ to delegate to the wrapped object.
class A:
def __init__(self):
self.objects = []
self.current_object = 0
def add_object(self, obj):
self.objects.append(obj)
self.current_object = obj
def __getattr__(self, name):
return getattr(self.current_object, name)
class B:
def __init__(self):
self.var = 10
def set_var(self, new_var):
self.var = new_var
a_obj = A()
b_obj1 = B()
b_obj2 = B()
a_obj.add_object(b_obj1)
a_obj.add_object(b_obj2)
a_obj.set_var(100)
print(b_obj2.var)
That will print "100".
You will still get an AttributeError if the wrapped object doesn't have the expected method.
It was interesting to look at this, it is intentionally rough but it does indeed allow you to call one the B instance's set_var methods.
The code below uses sets as a quick and dirty way to see the difference in callable methods, and if there is; it sets the attribute based on that name. Binding the method to the A instance.
This would only bind set_var once from the first object given.
def add_object(self, object):
self.objects.append(object)
B_methods = set([m for m in dir(object) if callable(getattr(object, m))])
A_methods = set([m for m in dir(self) if callable(getattr(self, m))])
to_set = B_methods.difference(A_methods)
for method in to_set:
setattr(self, method, getattr(object, method))

Shared class int across subclasses

I want to have a counter which increments every time a subclass is instantiated. How would I achieve this such that the last statement below evaluates to True:
class Abstract(ABC):
counter = 0
class A(Abstract):
pass
class B(Abstract):
pass
a = A()
b = B()
a.counter += 1
b.counter == 1
Currently each subclass gets its own counter, rather than sharing the one outlined in the superclass.
Would this work for you?
global_counter = 0
class Abstract:
def __init__(self):
global global_counter
global_counter += 1
class A(Abstract):
def __init__(self):
super().__init__()
class B(Abstract):
def __init__(self):
super().__init__()
a = A()
b = B()
print(global_counter) # (output: 2)
So I've implemented several different ways to achieve what I wanted:
Using a global keyword as suggested by #nihilok
Creating a custom Counter class to handle the integer value (essentially a fancy int).
using an int type in the super class and having its methods directly reference it using the super class's name instead of simply using the cls passed in during a class method.
My favourite (least amount of extra parts and most canonical to how I tend to write my objects) was the last method. Where the above translates to something like:
lass Abstract(ABC):
counter = 0
#staticmethod
def increment():
Abstract.counter += 1. # instead of cls.counter += 1
class A(Abstract):
pass
class B(Abstract):
pass
a = A()
b = B()
a.increment()
b.increment()
a.counter == b.counter # now true.

How to get object attributes to update dynamically in Python

I'd like to create a class that has 2 input attributes and 1 output attribute such that whenever one of the input attributes are modified the output attribute is modified automatically
I've tried defining the attributes as instance variables within and outside the constructor function but in either case, after instantiating the object, the output attribute remains fixed at the value set at the moment of instantiation
class Example():
def __init__(self,n):
self.name=n
inA=1
inB=1
if inA==1 and inB==1:
outA=1
else:
outA=0
when instantiated outA is set to 1 as expected
but if I try to update:
object.inA=0
object.outA remains 1 whereas I need it to be updated to 0
Trying to avoid the use of functions if possible. New to python and OOP so sorry if this question is nonsensical or has an obvious answer
If you want instance attributes that depend on other instance attributes, properties are the way to go.
class Example:
def __init__(self, n):
self.name = n
self.inA = 1
self.inB = 1
#property
def outA(self):
return self.inA and self.inB
You access outA like a regular instance attribute, obj.outA.
>>> my_obj = Example("example")
>>> my_obj.outA
1
Changing the attributes inA and inB affect outA.
>>> my_obj.inA = 0
>>> my_obj.outA
0
You can create a function in the class and some other minor changes:
class Example():
def __init__(self,n):
self.name=n
self.inA=1
self.inB=1
def f(self):
if self.inA==1 and self.inB==1:
self.outA=1
else:
self.outA=0
To call it:
a = Example('foo')
a.inA = 0
a.f()
print(a.outA)
Output:
0
As you can see, taking out:
a.f()
line would make it give an error:
AttributeError: 'Example' object has no attribute 'outA'
Do you want it to return your output?
Expanding on U9-Forward's answer:
class Example():
def __init__(self,n):
self.name = n
self.inA = 1
self.inB = 1
def f(self):
return self.inA and self.inB

Python Fix Dependancy Cycle

I'm working on a game using python.
The AI in the game uses variables that the player has, and vice versa.
For an example:
class Player():
def __init__(self, canvas...):
self.id = canvas.create_rectangle(...)
...
def touching_AI(self):
aipos = canvas.coords(AI object)
pos = canvas.coords(self.id)
...
#the function above checks if the player is touching the AI if it
#is, then call other functions
this = player(canvas...)
class AI():
def __init__(self, canvas...):
self.id = canvas.create_rectangle(...)
def chase_player(self):
playerpos = canvas.coords(this.id)
pos = canvas.coords(self.id)
...
# a lot of code that isn't important
Obviously, Python says that the AI object in the player class isn't defined. Both classes depend on the other to work. However, one isn't defined yet, so if I put one before the other, it returns an error. While there is probably a workaround for these two functions only, there are more functions that I didn't mention.
In summary, is there a way (pythonic or non-pythonic) to use and/or define an object before it is created (i.e even making more files)?
you do not
instead use arguments
class Player():
def __init__(self, canvas...):
self.id = canvas.create_rectangle(...)
...
def touching(self,other):
aipos = canvas.coords(other.object)
pos = canvas.coords(self.id)
...
#the function above checks if the player is touching the AI if it
#is, then call other functions
class AI():
def __init__(self, canvas...):
self.id = canvas.create_rectangle(...)
def chase(self,player):
playerpos = canvas.coords(player.id)
pos = canvas.coords(self.id)
then
player = Player(canvas...)
ai = AI(...)
ai.chase(player)
player.touching(ai)
but even better is to define a base object type that defines your interface
class BaseGameOb:
position = [0,0]
def distance(self,other):
return distance(self.position,other.position)
class BaseGameMob(BaseGameOb):
def chase(self,something):
self.target = something
def touching(self,other):
return True or False
then all your things inherit from this
class Player(BaseGameMob):
... things specific to Player
class AI(BaseGameMob):
... things specific to AI
class Rat(AI):
... things specific to a Rat type AI
You do not have a dependency cycle problem. But, you have the following problem,
You are trying it use an AI object, but you did not create the object anywhere. It needs to look like,
foo = AI() #creating the object
bar(foo) #using the object
The syntax is wrong around canvas.coords(AI object).
The way to call a function is foo(obj) without the type.
When defining a function you can optionally mention the type like def foo(bar : 'AI'):
The proof you can depend classes on each other, https://pyfiddle.io/fiddle/b75f2de0-2956-472d-abcf-75a627e77204/
You can initialize one without specifying the type and assign it in afterwards. Python kind of pretends everyone are grown-ups so..
e.g.:
class A:
def __init__(self, val):
self.val = val
self.b = None
class B:
def __init__(self, a_val):
self.a = A(a_val)
a_val = 1
b = B(1)
a = b.a
a.b = b

Store instance of class A in instance of class B

I have a question which is more regarding OOP in general rather than python specific.
Is ist possible to store instances of ClassA in instance of ClassB without a specific method, i.e. by some kind of inheritance.
Example: let's say I have one Model class and one Variable class
class Model():
def __init__(self):
self.vars = []
def _update_vars(self,Variable):
self.vars.append(Variable)
class Variable(Model):
def __init__(self,**kwargs):
self.__dict__.update(kwargs)
Is it now possible to call _update_vars whenever an instance of variable is being created.
So if I do something like this:
mdl = Model()
varA = Variable(...)
varB = Variable(...)
that mdl.vars would now include varA and varB.
I know that I could easily do this by passing the variables as an argument to a "public" method of Model. So I am not looking for
mdl.update_vars(varA)
So my two questions are:
is this possible?
if yes: would this very non-standard OOP programming?
Thanks for your help!
That's not how class inheritance is supposed to work. You only want to inherit something if the child class is going to make use of a good amount of the attributes/methods within the parent class. If the child class has a markedly different structure it should be a class of its own.
In either case, as mentioned by #jasonharper, at some point you would need to give direction as to which Variable instance belongs in which Model instance, so you're likely to end up with something like these:
varA = Variable(mdl, ...)
# or this
mdl.varA = Variable(...)
With the first way, you would maintain the method on your Variable class:
class Foo:
def __init__(self):
self.vars = []
class Bar:
def __init__(self, foo_instance, **kwargs):
foo_instance.vars.append(self)
f = Foo()
b = Bar(f, hello='hey')
f.vars
# [<__main__.Bar object at 0x03F6B4B0>]
With the second way, you can append the Variable instances into a list each time it's added:
class Foo:
def __init__(self):
self.vars = []
def __setattr__(self, name, val):
self.__dict__.update({name: val})
if not name == 'vars': # to prevent a recursive loop
self.vars.append(val)
f = Foo()
f.vars
# []
f.a = 'bar'
f.vars
# ['bar']
Of course, an easier way would be to just look directly into the __dict__ each time you want vars:
class Bar:
#property
def vars(self):
# Or you can return .items() if you want both the name and the value
return list(self.__dict__.values())
b = Bar()
b.a = 'hello'
b.vars
# ['hello']
Both of these will work the same even if you assigned the attributes with your own class instances.
You can use super() for this and pass the instance to the parent
class Model():
vars = []
def __init__(self, other=None):
if other:
self.vars.append(other)
class Variable(Model):
def __init__(self, a):
self.a = a
super().__init__(self)
mdl = Model()
varA = Variable(3)
varB = Variable(4)
print(mdl.vars)

Categories