Python object conversion - python

Assume that we have an object k of type class A. We defined a second class B(A). What is the best practice to "convert" object k to class B and preserve all data in k?

This does the "class conversion" but it is subject to collateral damage. Creating another object and replacing its __dict__ as BrainCore posted would be safer - but this code does what you asked, with no new object being created.
class A(object):
pass
class B(A):
def __add__(self, other):
return self.value + other
a = A()
a.value = 5
a.__class__ = B
print a + 10

a = A() # parent class
b = B() # subclass
b.value = 3 # random setting of values
a.__dict__ = b.__dict__ # give object a b's values
# now proceed to use object a
Would this satisfy your use case? Note: Only the instance variables of b will be accessible from object a, not class B's class variables. Also, modifying variables in a will modify the variable in b, unless you do a deepcopy:
import copy
a.__dict__ = copy.deepcopy(b.__dict__)

class A:
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, parent_instance, c):
# initiate the parent class with all the arguments coming from
# parent class __dict__
super().__init__(*tuple(parent_instance.__dict__.values()))
self.c = c
a_instance = A(1, 2)
b_instance = B(a_instance, 7)
print(b_instance.a + b_instance.b + b_instance.c)
>> 10
Or you could have a sperate function for this:
def class_converter(convert_to, parent_instance):
return convert_to(*tuple(parent_instance.__dict__.values()))
class B(A):
def __init__(self, *args):
super().__init__(*args)
self.c = 5
But using the 2nd method, I wasn't able to figure out how to pass additional values

Related

How to find an attribute in self.__init__?

class C:
def __init__(self):
self.a = 1
self.b = 2
def __setattr__(self,name,value):
if a in self.__init__: #Determine if a is an instance of __init__ function
do something
The above code will return an error and says
if name in self.__init__:
TypeError: argument of type 'method' is not iterable
If I don't iterate through self.__init__ function, how else am I supposed to know what attributes are defined in self.__init__ function?
If an attribute is set in init, I want to set the name prefixed by "somestring_" and append it to self__dict__: e.g., if I print self.__dict__ after self.__setattr__, it will print {'somestring_a': 1, 'somestring_b': 2}
Add an attribute that lists the attributes that are set in __init__, and use that.
class C:
predefined = ['a', 'b']
def __init__(self):
self.a = 1
self.b = 2
def __setattr__(self,name,value):
if name in self.predefined:
do something
else:
do something else
Another option would be to copy the keys of self.__dict__ at the end of the __init__ method:
class C:
def __init__(self):
self.a = 1
self.b = 2
self.predefined = set(self.__dict__)
def __setattr__(self,name,value):
if name in self.predefined:
do something
else:
do something else
class C():
def __init__(self, a, b):
self.a = a
self.b = b
a = C(1, 2)
print(a.__dict__)
>>> {'a': 1, 'b': 2}
So __dict__.keys() will give you the list of attributes ...
BUT :::
if you will check the list of your attributes in __setattr__ , you have to keep in mind that this function is also called when you do a = C(1, 2) so you shouldn't check your attributes in this level of code.

Basic confusion with classes & modules (Specially with "self")

I thought that this code would work
class A:
def __init__(self):
self.x = 1
B = self.create_b()
print(B.y)
def create_b(self):
class B:
def __init__(self):
self.y = self.x
return B
A = A()
but I receive the following error
AttributeError: type object 'B' has no attribute 'y'
What am I doing wrong?
You're confusing classes with class instances (not Python modules). In Python class statements are executable and create a callable object that you must then be called to create instance objects of the class that was defined.
Regular methods of a class automatically receive a first argument that's the instance they belong to, and by convention, this argument is usually called self.
Here's what I mean:
class A:
def __init__(self):
self.x = 1
B = self.create_b() # Create B class.
b = B(self) # Create instance of B class passing this instance of A.
print(b.y)
def create_b(self):
class B:
def __init__(self, a_inst):
self.y = a_inst.x
return B
a = A() # -> 1
There are three problems with this code. The first is that since create-b returns a class object, not an instance of the class, B's __init__ was never run. You could solve this with
class A:
def __init__(self):
self.x = 1
B = self.create_b()
b = B()
print(b.y)
def create_b(self):
class B:
def __init__(self):
self.y = self.x
return B
A = A()
The second is that nested classes do not have access to the wrapping method's local namespace like a nested function (closure) would. When attempting self.y = self.x, instances of class B have no special relationship with the instance of A that created them. You could solve this with
class A:
def __init__(self):
self.x = 1
B = self.create_b(self)
b = B()
print(b.y)
def create_b(self):
class B:
def __init__(self, a):
self.y = a.x
return B
A = A()
The third is that python creates a weakref to classes when they are defined that never goes away. Each time you call create_b, you create a small memory leak. You could solve this with
class A:
def __init__(self):
self.x = 1
b = B(self)
print(b.y)
class B:
def __init__(self, a):
self.y = a.x
A = A()

Access all variables by their names of specific object in class

I have two python classes:
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
Now I have an instance of B and need to access param1, I can just write b.a.param1. But the goal is to omit the 'a' part, so access this param with b.param1. I could add property to class B, but I am searching for generic solution - when A class has a lot variables. Is it possible? And would it be clean solution?
This is not the most elegant option and probably a bad practice but you can copy all the attributes of a to be attributes of b using getattr and setattr:
import inspect
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
variables = [i for i in dir(a) if not inspect.ismethod(i) and i[:2] != '__']
for var in variables:
setattr(self, var, getattr(a, var))
This way you can access a's attributes directly:
a = A(1)
b = B(a)
b.param1
which will return
1

How to access objects from a different class?

There are three classes :
A, B and C
The __init__ of B creates an object of A. Using the mutators, I will be able to change the attributes of A from B for the instance created.
However, I am not unable to find any way to use that instance of A created by B to be used in C without passing the Object explicitly to the __init__ method [ not C.__init(self, object: A) ]
Is there any way to implicitly allow C to use that instance of A ?
I am new to python and not sure if this a valid question. I have looked at other sources where it explicitly passes the object to class C
class A:
def __init__(self):
x = []
y = []
class C :
def __init__(self):
#[get obj1 without passing the instance in init]
self.value = None
def method1():
self.value = len([]) #len(obj1 of A.x)
class B:
def __init__(self):
obj1 = A()
obj1.x = [1,2,3,4]
obj1.y = [1,2,3]
obj2 = B()
print(obj2.value) #this should be the length of x in the instance A created above
Here is a simple example:
class A:
def __init__(self, i = ""):
self.item = i
class B:
def __init__(self):
self.a = A("hello")
class C:
def __init__(self):
b = B()
print(b.a.item)
c = C()
Output:
hello
Let's say we have classes A and B:
class A:
def hello_world(self):
print("hello world")
class B:
def __init__(self):
self.a = A()
def hello_world(self):
self.a.hello_world()
You create an instance of class B (which will create an instance of class A inside):
b = B()
You can then pass a reference to either b or b.a to any function of an instance of class C (either a constructor or not)
class C:
def hello_world(self, a):
a.hello_world()
c = C()
c.hello_world(b.a)
You can also use global variables:
class C:
def hello_world(self):
b.a.hello_world()
c = C()
c.hello_world()
Here the instances of class C will rely on variable b to be in place and just use its a attribute.
Using global variables in classes is generally considered to be hard to maintain and a bad practice. If your class depends on a value or an instance of some class you should pass the reference in the constructor (__init__ function) or in the function that's using it.
If these classes are in different different python files then you can also use these classes by importing the class name and creating an object of that class:
eg:
file1.py
class A:
def __init__(self):
x = []
y = []
file2.py
from file1 import A
class C :
def __init__(self):
[get obj1 without passing the instance in init]
self.value = None
self.obj_a = A()
def xyz(self):
print "in class c"
file3.py
from file2 import C
from file1 import A
Class B:
def __init__(self):
self.obj_a = A()
self.obj_c = C()
def another_func(self):
print self.obj_c.xyz()# it will print "in class c"

Accessing higher-level class from nested class in Python

Suppose I have two Python classes, A and B, and that B is an attribute of A. Can a method of B modify a property of A? for example, I would like to be able to call
A.B.setXinA(1)
A.x
>>> 1
One way around it would be embed a reference to A in B:
A.B.reftoA = A
But that's rather ugly... Is there a way to access the higher-level class directly? Below is a working example using the second method:
class A:
def __init__(self, b):
b.parent = self
setattr(self, b.name, b)
class B:
def __init__(self, name):
self.name = name
b = B('abc')
a = A(b) # b is now a.abc
abc.parent.x = 1
a.x
>>> 1
What about a method in B like this:
class B:
def __init__(self, name):
self.name = name
def setXinA(self, x):
self.parent.x = x
Then:
>>> b = B('abc')
>>> a = A(b)
>>> b.setXinA(19)
>>> print(A.x)
19
This way requires that setXinA is called by an instance of B rather than just B.setXinA(42) for example. Also, it sets x as an attribue of the class A, rather than any particular instance of A.

Categories