Circular attributes in Python - python

I am working with several classes, with each having it's own attributes.
Trying to avoid to pass a lot of variables when calling sub_functions, I would rather call classe's attributes.
As an example, let's concider 2 classes such as :
class Class_A(object):
def __init__(self, element_b):
self.value_specific_to_a = 1
self.element_b = element_b
def element_a_can_do(self):
print(self.element_b.value_specific_to_b)
class Class_B(object):
def __init__(self):
self.element_a = None
self.value_specific_to_b = 2
def add_a(self, element_a):
self.element_a = element_a
def element_b_can_do(self):
print(self.element_a.value_specific_to_a)
item_b = Class_B()
item_a = Class_A(item_b)
item_b.add_a(item_a)
I think that it is pointer's addresses of those classes that are save to each other, but I wonder if it can cause any issue/leak in my code.

Related

How to implement a sole class of methods inside an external class

I have an external class to represent my data idk. Inside the class idk I want to have another class change which will contain various methods to change instances of the class idk. What I've tried is below. Note: this is a simplified example of my actual code
class idk:
def __init__(self):
self.x=1
self.y=2
class change:
def func(self):
self.x=10
self.y=100
var=idk()
var.change.func()
print(var.x, var.y)
However this gives the error:
TypeError: func() missing 1 required positional argument 'self'
How do I change the code to make it work?
Well, first of all, your are getting this error because you are accessing the func function as a class attribute and not by an instance of it (putting a class definition inside another class won't make it an instance).
If it makes sense, you cloud put those "change methods" in the idk class directly (that would be a normal approach):
class idk:
def __init__(self):
self.x = 1
self.y = 2
def func(self):
self.x = 10
self.y = 100
var = idk()
var.func()
print(var.x, var.y) # Output: 10 100
If you really want/need to separate those methods, you could do another class. The way I would implement that class is with static methods where all of them recieve an idk instance as the first parameter:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class idkChanger:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idkChanger.func(var)
print(var.x, var.y) # Output: 10 100
If you really really want/need to have that "changer" class inside of the idk class you can define it there, but this is not common at all. Also, you will have to pass the instance as well, that Changer class:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class Changer:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idk.Changer.func(var)
print(var.x, var.y) # Output: 10 100
Final notes:
You could not mark (decorate) the func as static and it will work the same, but this will bring more confution for several reasons (e.g., you would tecnically saying that func is an instance method. Which is not, because the objects you want to change are not Change's instances but idk's).

Duplication of class inside two different objects

I found a bug in my program and I did some digging before making something much simpler to understand the problem.
In a nutshell: I'm creating two objects from a class where I want to store an object from another class. But When I'm doing this, both objects are getting the same result. How can I prevent this duplication?
It seems like both objects are pointing on the same class.
# Sub-class that may not be extendable
class MyClass:
def __init__(self, number):
self.number = number
#classmethod
def add_number(cls, number=0):
return cls(number)
# Main class
class MyOtherClass:
my_objects = []
"""
Initialize the class
:param List[MyClass] my_objects: List of MyClass objects
"""
def __init__(self, this_object):
self.my_objects.append(this_object)
if __name__ == "__main__":
my_object1 = MyOtherClass(MyClass.add_number())
my_object2 = MyOtherClass(MyClass.add_number())
for i in range(100):
my_object1.my_objects.append(MyClass.add_number(i))
print(f"Appending obj1 : {my_object1.my_objects[i].number}")
for y in range(100, 0, -1):
my_object2.my_objects.append(MyClass.add_number(y))
print(f"Appending obj2 : {my_object2.my_objects[y].number}")
# later
z = 0
while z < len(my_object1.my_objects):
print(f"obj1 : {my_object1.my_objects[z].number}")
print(f"obj2 : {my_object2.my_objects[z].number}")
z += 1

Pythonic Mangling

I stumbled on mangling by accident - I put two underscores instead of one in a class function name - but have found it to be quite useful. For example, I have various objects that need some air traffic control between them so I can call their parent objects with the same function, i.e. parentobject.__remove(). It's not much different to use parentobject._remove_myclass() but I kinda like the mangling!
Mangling seems designed to protect parent class objects from being overridden so is exploiting this a) "pythonic" and more importantly b) reliable/a good idea?
class myClass():
def __mc_func(self):
print ('Hello!')
def _yetAnotherClass__mc_func(self):
print ('Mangled from yetAnotherClass!')
def new_otherClass(self):
return otherClass(self)
def new_yetAnotherClass(self):
return yetAnotherClass(self)
class otherClass():
def __init__(self, myClass_instance):
self.mci = myClass_instance
def func(self):
self.mci.__mc_func()
class yetAnotherClass():
def __init__(self, myClass_instance):
self.mci = myClass_instance
def func(self):
self.mci.__mc_func()
g = myClass()
h = g.new_otherClass()
try:
h.func()
except AttributeError as e:
print (e)
#'myClass' object has no attribute '_otherClass__mc_func'
j = g.new_yetAnotherClass()
j.func()
#Mangled from yetAnotherClass!

In python, how to share (the reference of) a variable as attributes across different objects?

So I have a main class and several auxiliary classes. In the main class's methods, I'll need some auxiliary objects, with which I want to "share" the main instance's shared_data attribute, so that whenever I change the shared_data attribute of the main object, the auxiliary objects' corresponding attributes will update accordingly too. (For concreteness, you may think of the shared_data as "time", and I want the "time" attribute to stay synced across the various object).
However, it seems that doing this in the naive way as below will not work, because that would not actually "share" the data across objects, but rather just assign the current value to these objects, so these object attributes would not stay synced. What should be the correct way to implement this "syncing" or "sharing" functionality?
class Main:
def __init__(self, shared_data):
self.shared_data = shared_data
pass
def do_stuff(self):
# initialise auxiliary objects (only for once).
# hopefully when self.x1 is changed, the data in
# the objects aux1, aux2 will also reflect this change
aux1, aux2 = Aux1(self.shared_data), Aux2(self.shared_data)
# however, in general, changing self.shared_data would not
# change the data in aux1 or aux2
another_value = ...
self.shared_data = another_value # doesn't work
pass
class Aux1:
def __init__(self, x1):
self.x1 = x1
...
pass
class Aux2:
def __init__(self, x2):
self.x2 = x2
...
pass
Everything is fine except this part:
self.shared_data = another_value # doesn't work
That line does not modify the shared_value. It just assigns a different object to the variable which was before that holding the shared data object.
What you need to do instead is to modify shared_value, for example like this:
self.shared_value.data = another_data
Here is a complete example:
class SharedData:
def __init__(self):
self.time = 0
self.colour = "red"
class ObjectWithSharedData:
def __init__(self, shared_data):
self._shared_data = shared_data
def do_stuff(self):
self._shared_data.time = 7
def get_time(self):
return self._shared_data.time
shared_data = SharedData()
a = ObjectWithSharedData(shared_data)
b = ObjectWithSharedData(shared_data)
c = ObjectWithSharedData(shared_data)
a.do_stuff()
print(a.get_time()) # prints 7
print(b.get_time()) # prints 7
print(c.get_time()) # prints 7

Trouble with specific class inheritance behaviour

So I am trying to get my data structure set up for an automated generator I am writing for a roleplaying game and I am having trouble with some specific inheritance quirks. Here is an excerpt of the data structure.
class data():
def __init__(self):
self.races = Races()
class Races(data):
def __init__(self):
self.humans = Humans()
class Humans(Races):
def __init__(self):
self.Characteristics = {
'Brawn':2,
'Agility':2,
'Intellect':2,
'Cunning':2,
'Willpower':2,
'Presence':2
}
There is a lot more in the structure but this is just a bottom to top overview. I also know it is indented weirdly but that is strictly stack overflow.
Now I wish to have two behaviors from this object.
The ability to call any characteristic with
data.races.humans.Characteristic['brawn']
as the calling format.
And too also be able to iterate through subclasses with a generator like:
(subclass for subclass in data.races.__subclasses__())
obviously after I have instantiated the object.
Now I have tried changing the structure several times and I can get it so EITHER I can call it with dot notation, but it returns AttributeError: 'Races' object has no attribute '__subclasses__'
Or vice versa by completely separating it into a more traditional structure but then I cannot call in dot notation and this makes it very hard to keep everything organized and readable.
Can anyone suggest what I am doing wrong or a more Pythonic way to approach the problem?
Let's start in the middle. Presumably, a character of any race has the same attributes, just different values for those attributes.
class Race:
def __init__(self):
self.life = 100 # 100% healthy
class Humanoid(Race):
def __init__(self):
super().__init__()
self.legs = 2
class Insectoid(Race):
def __init__(self):
super().__init__()
self.legs = 8
class Human(Humanoid):
def __init__(self):
super().__init__()
self.brawn = 2
self.agility = 2
self.intellect = 2
self.cunning = 2,
self.willpower = 2
self.presence = 2
class Elf(Humanoid):
def __init__(self):
super.__init__()
self.brawn = 1
self.agility = 3
self.intellect = 3
self.cunning = 2
self.willpower = 3
self.presence = 1
Now, any particular character would be instantiated as the correct class:
some_elf_1 = Elf()
some_human_1 = Human()
some_human_2 = Human()
for character in [some_elf_1, some_human_1, some_human_2]:
print("Brawn: ", character.brawn)
In the preceding, it doesn't matter what the actual type of each character is; as long as you know that it is some subclass of Race (or an instance of Race itself), it will have a brawn attribute that you can access.
You data class doesn't really seem necessary without more detail.
So, While the answer given put me on the right track I realized what I needed and am just throwing in my lot for any poor souls.
Firstly - I realized what was wrong with my generator, I was calling on the initialized object instead of the class object. Objects do not have a subclasses attrib and I was mis-informed by most of the guides I read!
Secondly, I considered using a metaclass to get the iterating behavior I wanted from my objects can simply be achieved with a registry attribute that is a dict of all the initialized subclasses.
lass Races(data):
def __init__(self):
self.humans = Humans()
self.droids = Droids()
self.twileks = Twileks()
self.registry = {
'humans':self.humans,
'droids':self.droids,
'twileks':self.twileks
}
This allows me to iterate through certain values for different races after they have been initialized.
Thanks for all the great answers!

Categories

Resources