Finding object references in Python - python

I have written following method to get a parent container of an object. The object may be referenced by multiple containers, so I also provide container class names to that method in the order correct with nesting order.
def get_parent(element, *argv):
def find_object_in_referrer(element, parent_class):
gc.collect()
for referrer in gc.get_referrers(element):
if parent_class.__name__ not in referrer:
continue
for key, object in referrer.items():
if not isinstance(object, parent_class):
continue
return object
return None
return None
for parent_class in argv:
object = find_object_in_referrer(element, parent_class)
if not object:
None
element = object
return element
It works as follows:
class Parent(object):
def __init__(self, child):
self.child = child
class Parent2(object):
def __init__(self, child):
self.child = child
class Parent3(object):
def __init__(self, child):
self.child = child
child = Child()
parent = Parent(child)
parent2 = Parent2(parent)
parent3 = Parent3(parent2)
get_parent(child, Parent, Parent2, Parent3)
The last line, get_parent() call, should return reference to parent3 object.
That code sometimes works while debugging but never works in a normal execution.
Why get_referrers() function behaves different while in debugging mode?
I know this is not recommended to use that function. Is there any other method to achieve the same - implement a function to get a parent container?

Related

How to communicate between sibling objects in Python, when using Composition, not Inheritance

I have a parent object which is composed of 2 (different) child objects.
The 2 child instances need to communicate. For example, assume child1 needs to send something to child2:
import children
class Parent:
def __init__(self):
self.child1 = children.Child1(self.q)
self.child2 = children.Child2(self.q)
parent = Parent()
parent.child1.send("string")
Is there a recommended pattern to achieve this?
The best I could come up with was to create a Queue between the 2 objects. This works, but it requires the receiving object to run a thread. For example:
parent.py:
import children
import queue
import time
class Parent:
def __init__(self):
self.q = queue.Queue()
self.child1 = children.Child1(self.q)
self.child2 = children.Child2(self.q)
parent = Parent()
parent.child1.send("string")
time.sleep(1)
children.py:
import threading
class Child1:
def __init__(self, q):
self.q = q
def send(self, item):
self.q.put(item)
class Child2:
def __init__(self, q):
self.q = q
self.receiver = threading.Thread(target=self.worker, daemon=True).start()
def worker(self):
"""Process the queue"""
while True:
item = self.q.get()
print(f"{item} received")
In practice, the "items" I send in the queue are a function name and an argument list. This is basically the Command pattern as described here. But I don't like the need for receiver threads.
I would prefer if it was possible to allow one object to directly call a method in the other object.
If there was an inheritance relationship between them, with a common parent, I could maybe use super() for this:
class Child1:
def send(self, function, arguments):
super().child2.function(arguments)
But there is no inheritance in my case: just composition.
Is there a better way?
Just construct the children with a reference back to the parent:
class Child1:
def __init__(self, parent):
self.parent = parent
def send(self, msg):
self.parent.child2.print_out(msg)
class Child2:
def __init__(self, parent):
self.parent = parent
def print_out(self, msg):
print(msg)
class Parent:
def __init__(self):
self.child1 = Child1(self)
self.child2 = Child2(self)
parent = Parent()
parent.child1.send("foo")
I believe what you're looking for is the Façade Pattern.
Presumably, Parent is more than just a namespace; it knows things and does stuff. Having Child1 send something to Child2 seems like it would be a behaviour of Parent, the implementation of which is abstracted away, hidden behind the façade.
class Foo:
def send_msg(self):
return f'This is a message sent from {self.__class__.__name__}.'
class Bar:
def receive_msg(self, msg):
print(self.__class__.__name__, 'has received the following message:'
print('\t', msg)
class Facade:
def __init__(self):
self.foo = Foo()
self.bar = Bar()
def foo_the_bar(self):
self.bar.receive_msg(self.foo.send_msg())
>>> facade = Facade()
>>> facade.foo_the_bar()
Bar has received the following message:
This is a message sent from Foo.

Attribute inherited from child class prints child object not string from parent

I know many topics around class inheritance in Python have been addressed, but I could not find a thread that addressed this specific issue.
Edit: I'm running Python 3.5.5.
Code:
class Parent():
def __init__(self, parentParam="parent param"):
self.parentParam = parentParam
class Child(Parent):
def __init__(self, childParam = "child param"):
self.childParam = childParam
super().__init__(self)
child = Child()
print(child.childParam)
print(child.parentParam)
Output:
child param
<__main__.Child object at 0x0000017CE7C0CAC8>
Why does does child.parentParam return the child object and not the string "parent param"? I feel like it should print out the default string set for the Parent class. This appears to be the same syntax as I have been following in this tutorial.
Thanks, everyone.
Because you provide the instance of child (aka self) to the super call:
class Child(Parent):
def __init__(self, childParam = "child param"):
self.childParam = childParam
super().__init__(self) # here you override the default by supplying this instance
Use:
class Child(Parent):
def __init__(self, childParam = "child param"):
self.childParam = childParam
super().__init__()
instead and you get this output instead:
child param
parent param
Your call to super is wrong. It's not necessary to explicitly pass a self argument when using it to delegate a method call to a superclass (see the typical usage example shown in the documentation).
In Python 3, calls to super() with no arguments are equivalent to super(CurrentClass, self).method(arg)—which was the only way it could be done in Python 2—making it no longer necessary to specify it at all when making calls to superclass methods.
So what's happening, since you passed it in your code, is that it gets interpreted as overriding the default value specified for the parentParam argument.
Here's doing it properly and the result:
class Parent:
def __init__(self, parentParam="parent param"):
self.parentParam = parentParam
class Child(Parent):
def __init__(self, childParam="child param"):
self.childParam = childParam
super().__init__() # NO NEED TO PASS self.
child = Child()
print(child.childParam) # -> child param
print(child.parentParam) # -> parent param

python parent child relationship class

I've written up a class as seen below. I want to add the attribute of 'parent' to my base class Node. I was wondering if someone could show me how to properly do this. I've been given guidance on how to do it but I'm not entire sure how to write it syntax wise. Here is the suggested way to do it...
generally I would hide the parent attribute behind a property so when
its set, the children array of the previous parent can be modified so
if you say n.parent = x, it actually remove node n from it's parent
and set the parent value
class Node(object):
def __init__(self, name, attributes, children):
self.name = name
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
class Camera(Node):
def __init__(self, name="", attributes=None, children=None, enabled=True):
super(Camera, self).__init__(name=name, attributes=attributes, children=children)
self.enabled = enabled
updated
import weakref
class Node(object):
_parent = None
def __init__(self, name, attributes, children, parent):
self.name = name
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
self.parent = parent
for child in children:
child.parent = self
#property
def parent(self):
return self._parent() if self._parent is not None else None
#parent.setter
def parent(self, newparent):
oldparent = self.parent
if newparent is oldparent:
return
if oldparent is not None:
oldparent.children.remove(self)
if self not in newparent.children:
newparent.children.append(self)
self._parent = weakref.ref(newparent) if newparent is not None else None
class Camera(Node):
def __init__(self, name="", attributes=None, children=None, enabled=True, parent=None):
super(Camera, self).__init__(name=name, attributes=attributes, children=children, parent=parent)
self.enabled = enabled
Camera()
Example code, incorporating weakref to avoid reference cycles that can delay cleanup (or prevent it entirely in some cases, particularly on Python 3.3 and earlier):
import weakref
class Node:
# If this is Python 2, you need to explicitly inherit from object to
# be a new-style class with descriptor support (which allows properties), so
# the class line would be:
# class Node(object):
# On Py3, it's implicit and can be omitted
# Ensure attribute readable so getter/setter don't need to use has/getattr
# Will be allocated per-instance when self.parent is assigned in __init__
# So on Py3.3+, it will still get the compact key-sharing dicts for attributes
_parent = None
# Adding defaults for all values matching Camera for simplicity
def __init__(self, name='', attributes=None, children=None, parent=None):
self.name = name
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
self.parent = parent
for child in children:
child.parent = self
#property
def parent(self):
return self._parent() if self._parent is not None else None
#parent.setter
def parent(self, newparent):
oldparent = self.parent
# If setting to existing parent, then no-op
# Remove this check and early-out if you want to be able to move
# a node to the end of its parent's children by reassigning the same parent
if newparent is oldparent:
return
if oldparent is not None:
oldparent.children.remove(self)
if self not in newparent.children:
newparent.children.append(self)
self._parent = weakref.ref(newparent) if newparent is not None else None
Typically, to avoid issues with changing parent class prototypes, I put additional parameters to child class __init__ methods first, not last. Because I gave __init__ defaults on Camera, this makes Camera very simple:
class Camera(Node):
def __init__(self, enabled=True, *args, **kwargs):
super().__init__(*args, **kwargs)
# On Py2, super isn't magic, so you need to be explicit unlike Py3:
# super(Camera, self).__init__(*args, **kwargs)
self.enabled = enabled
As you can see, by moving the Camera unique __init__ param to the front, Camera can stop paying attention to changes in the Node __init__; the new Camera works with the original Node or the new Node (that accepts parent and assigns self.parent) just fine, because it's less tightly coupled to the exact parameter ordering. Note that this does mean that if enabled is not passed positionally, then all arguments must be passed by keyword.
Please comment if I made any mistakes, but that should be close to correct. In general, I had the Node class use the parent accessor to simplify the code by removing the difficulty with handling None properly (None is not weak referencable).

python: set read-only attribute for class objects

I've created a class object called 'Node'. I then created two subclasses 'Beetle' and 'Dakota'. You'll notice there is an attr called 'superclass' which is part of the baseclass. I want this attribute to be set for each subclass and once it's set it should never be changed. It's a read-only attribute. I was wondering how to set this attribute up correctly in order to be a read only attribute?
class Node(object):
def __init__(self, name, superclass, attributes, children):
self.name = name
self.superclass = superclass
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
class Beetle(Node):
def __init__(self, name="", superclass="Cars", attributes=None, children=None, enabled=True):
super(Beetle, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled
class Dakota(Node):
def __init__(self, name="", superclass="Trucks", attributes=None, children=None, enabled=True):
super(Dakota, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled
Rename you variable to add __ to the begining
self.__superclass = superclass
you can't access self.__superclass with something like Dakota().__superclass
if you want to get value of __superclass add a function in Node class to return it.
def getsuperclass(self):
return self.__superclass
...
Dakota().getsuperclass()
To have a 'read-only' property in a class you can make use of the #property decoration.
An example:
class Dakota(object):
def __init__(self, superclass):
self.__superclass = superclass
#property
def superclass(self):
return self.__superclass
Usage:
>>> a = Dakota('lol')
>>> a.superclass
'lol'
>>> a.superclass = 'hah'
AttributeError...
AttributeError: can't set attribute
You can refer to this other answer thread

Access parent class instance attribute from child class instance?

How to access "myvar" from "child" in this code example:
class Parent():
def __init__(self):
self.myvar = 1
class Child(Parent):
def __init__(self):
Parent.__init__(self)
# this won't work
Parent.myvar
child = Child()
Parent is a class - blue print not an instance of it,
in OOPS to access attributes of an object it requires instance of the same,
Here self/child is instance while Parent/Child are classes...
see the answer below, may clarify your doubts.
class Parent():
def __init__(self):
self.myvar = 1
class Child(Parent):
def __init__(self):
Parent.__init__(self)
# here you can access myvar like below.
print self.myvar
child = Child()
print child.myvar
Parent does not have an attribute called myvar. Only instances of parent have that attribute. From within a method of Child, you can access that attribute with self.myvar.
Alternative to using inheritance
The current answers are coming from an inheritance perspective, but this isn't always what you want -- sometimes you might want the child to be an entirely different type of object to the parent, but that still has access to the parent attributes.
For a business analogue, think of Excel Workbooks which have Worksheet children, which themselves have Range children, and so on.
Only Child
An alternative approach (and not the only one) is to pass the parent as an argument to the child to create a property that corresponds to the parent:
class Parent(object):
def __init__(self, parent_value):
self.parent_value = parent_value
self.child = Child(self)
class Child(object):
def __init__(self, _parent):
self.parent = _parent
self.child_value = 0
new_parent = Parent(1)
print(new_parent.parent_value) # prints 1
new_child = new_parent.child
print(new_child.child_value) # prints 0
print(new_child.parent.parent_value) # prints 1
new_parent.parent_value = 100
print(new_child.parent.parent_value) # prints 100
Note that this instantiates the child at the same that that new_parent is instantiated. To access the parent's attributes, just go through the parent property.
Multiple Children
You could extend this so that you can create multiple instances of the Child class through the new_parent object. The code below is one simple way of doing this which replaces the child property with a children property and an add_child method.
class Parent(object):
def __init__(self, parent_value):
self.parent_value = parent_value
self.children = []
def add_child(self, child_value):
new_child = Child(child_value, _parent=self)
self.children.append(new_child)
return new_child # allows add_child to assign a variable
class Child(object):
def __init__(self, child_value, _parent):
self.parent = _parent
self.child_value = child_value
new_parent = Parent(1)
# add 3 Child instances with child_values 2, 4, 8
[new_parent.add_child(v) for v in [2, 4, 8]]
# add another child that utilises the return statement
extra_child = new_parent.add_child(16)
for child in new_parent.children:
print(child.child_value) # prints 2, 4, 8, 16
print(child.parent.parent_value) # prints 1
new_parent.parent_value = 32
for child in new_parent.children:
print(child.parent.parent_value) # prints 32
# prove that extra_child is new_parent.children[3]
extra_child.child_value = 64
print(new_parent.children[3].child_value) # prints 64
You need to initiate the parent class first via so-called proxy object using command "super".
So the code will be like this:
class Parent():
def __init__(self):
self.myvar = 1
class Child(Parent):
def __init__(self):
super.__init__()
child = Child()
print child.myvar

Categories

Resources