Why doesn't imported class attribute change in Python? - python

I am trying to understand the mechanism of __import__(fromlist=['MyClass']).
Imagine I have several classes WhiteBox:
class WhiteBox:
def __init__(self):
self.name = "White Box"
self.color = None
def use(self, color):
self.paint(color)
def paint(self, color):
self.color = color
I am importing these classes with __import__(fromlist=['WhiteBox']) statement.
I decide to repaint all boxes with the same color and create a loop:
for box in imported_boxes:
box.WhiteBox().use = "green"
print("REPAINTED:", box.WhiteBox().name, box.WhiteBox().color)
When I try to access box.WhiteBox().color attribute, I still get None.
REPAINTED: WhiteBox None
I expected that __import__ would allow to manipulate the object as if it was instantiated, it appears not true. How do I solve this issue?

Use are using "use" as a property but it it defined as a function:
box = box.WhiteBox() = "green"
#change it to:
box.WhiteBox().use("green")
Next problem:
You are creating WhiteBox again and again so it will always have the initial None Value...
box.WhiteBox().use("green") #created once
print("REPAINTED:", box.WhiteBox().name, box.WhiteBox().color) #two more times...

Related

Python design question: When dependencies can't be instantiated ahead of time

Sorry for the poor title but if I knew the class of problems I was tackling perhaps I could find an answer.
In an ideal situation, one creates an object with all its needed dependencies, and then uses the object. However in some cases, the information needed to create a dependency is not known before the object is used.
I will try to create a concrete example that has to do with making a house. The following will be the dependencies
class WallMakerAlgorithm(abc.ABC):
def build(self):
# Put specific algorithm here that will build the wall
pass
class DoorMakerAlgorithm(abc.ABC):
def __init__(self, color: float, material: str):
# subclasses can override init to add more fancy stuff
self.color = color
self.material = material
def build(self):
# Put specific algorithm here that will build the door
pass
These are used by a house builder class. In the ideal case, a HouseBuilder will know its dependencies on init:
class BasicHouseBuilder:
def __init__(self, door_maker_algorithm: DoorMakerAlgorithm, wall_maker_algorithm: WallMakerAlgorithm):
self.door_maker_algorithm = door_maker_algorithm
self.wall_maker_algorithm = wall_maker_algorithm
def build(self):
self.wall_maker_algorithm.build()
self.door_maker_algorithm.build()
But now assume that for some (contrived) reason, the customer hasn't decided on the color of the doors. They tell you that once the wall is built, they will know the color because they will be able to see it. This creates a chicken and egg kind of problem. I can't create the HouseBuilder because I can't create the door making algorithm, and I can't create the door making algorithm without actually running HouseBuilder. Something like this:
class DecideColorLaterHouseBuilder:
def __self__(self, wall_maker_algorithm: WallMakerAlgorithm, query_color: Callbale, ???):
self.wall_maker_algorithm = wall_maker_algorithm
self.query_color = query_color
???
def build(self):
self.wall_maker_algorithm.build()
desired_door_color = self.query_color()
# Now I want to build the door. But I don't have the door algorithm because I don't know the color!
Is there some design pattern that deals with this kind of problem? Are my abstractions just messed up in general? I can potentially add color as an argument to build of the DoorMakingAlgorithm but that just messes up my abstractions.
You could use default values for the arguments of your functions.
class DoorMakerAlgorithm(abc.ABC):
def __init__(self, material: str, color: float=0xffffff):
# arguments with default values (optional arguments) must come after mandatory ones
# Here I put white color, but you could put any other color, or even None
# subclasses can override init to add more fancy stuff
self.color = color
self.material = material
Using None as a default value can allow you to through an exception in case the user still hasn't define the color when you build
class DoorMakerAlgorithm(abc.ABC):
def __init__(self, material: str, color: float=None):
self.color = color
self.material = material
def build(self):
if self.color is None:
raise Exception(f"Color hasn't been defined yet for {self}")
# Then proceed to build the wall
You can also use a property so that the user can change the color after instanciation while you still check the input. Note that in python, the user can always access any attribute of a class and modify them. The property allows you to secure the input, but the user can still modify the _color attribute and put anything in there. the _ is a convention for attributes the end user shouldn't mess with.
class DoorMakerAlgorithm(abc.ABC):
def __init__(self, material: str, color: float=None):
self.color = color
self.material = material
# You can use a property to allow the user to change the color later, and ensure input is correct
#property
def color(self):
return self._color
#color.setter
def color(self, color: float=None):
self._color = color
def build(self):
if self.color is None:
raise Exception(f"Color hasn't been defined yet for {self}")
# Then proceed to build the wall
algo = DoorMakerAlgorithm("bricks") # not giving a color
# algo.build() # would through an exception
algo.color = 0xffffff
algo.build() # works

Function to set properties of an object of a class composition

I would like construct a class composition that includes a function set_props for setting the instance variables of components.
The application for this is in defining new objects for drawing in matplotlib. One example is that I would like to have a function drawMyArrow that draws an arrow with possibly different colors (and other specifications) for its head, tail, and arc. I would like to be able to pass various specifications for the head, tail, and arc via keyword arguments in drawMyArrow. I haven't worked with classes before, but reading up on this online, I believe that the best way to solve my problem is to define a class MyArrow that is a composition of some classes ArrowHead and ArrowArc.
To illustrate my problem, I am using a toy example (that I also used for a previous question here). Let's define a class Room that is a composition of the classes wall, window, and door.
class Door:
def __init__(self, color='white', height=2.3, width=1.0, locked=True):
self.color = color
self.height = height
self.width = width
self.locked=locked
class Window:
def __init__(self, color='white', height=1.0, width=0.8):
self.color = color
self.height = height
self.width = width
class Wall:
def __init__(self, color='white', height=2.5, width=4.0):
self.color = color
self.height = height
self.width = width
class Room:
def __init__(self):
self.door = Door()
self.window = Window()
self.wall = Wall()
If I want to have a function for Room that can set properties of its components, I can do something like this:
def set_windowprops(r, color=None, width=None, height=None):
if not color==None: r.window.color=color
if not width==None: r.window.widht=width
if not height==None: r.window.height=height
return r
But if I decide to add more instance variables to Window, I would have to go back to this function and add the new instance variables. Can I write set_windowprops so that it automatically accepts all instance variables of Window as keywords?
Ideally, I would like to write a function like this:
def set_props(r, windowcolor=None, windowwidth=None, windowheight=None,
doorcolor=None, doorwidth=None, doorheight=None, doorlocked=None,
wallcolor=None, wallwidth=None, wallheight=None):
if not windowcolor==None: r.window.color=windowcolor
if not windowwidth==None: r.window.widht=windowwidth
if not windowheight==None: r.window.height=windowheight
if not doorcolor==None: r.door.color=doorcolor
if not doorwidth==None: r.door.widht=doorwidth
if not doorheight==None: r.door.height=dooorheight
if not doorlocked==None: r.door.locked=doorlocked
if not wallcolor==None: r.wall.color=wallcolor
if not wallwidth==None: r.wall.widht=wallwidth
if not wallheight==None: r.wall.height=wallheight
return r
but without the need of hardcoding all instance variables of components into the function.
I was looking into using keyword dictionaries like so:
window_vars = getNamesOfInstanceVariables(Window) #TODO
window_kwargs = {}
for v in window_vars:
window_kwargs[v] = None
def set_windowprops(r, **kwargs):
for kw in kwargs:
if not kwargs[kw]==None:
r["window"][kw] = kwargs[kw] #TODO
return r
Two issues keep me from getting this to work:
(1) In the last line, I am pretending that r is a dictionary (of dictionaries) and using dictionary syntax to assign a value to r.window.kw. But that doesn't work because r is an instance of Room and not a dictionary. What would be the syntax for setting instance variables if the name of the component class and the name of the instance variable are given as strings?
(2) I have tried using inspect to write getNamesOfInstanceVariables, but I am unable to get it to work robustly. Many classes in matplotlib inherit from base classes. I would like getNamesOfInstanceVariables to return all instance variables that a user can set for an object of this class. For example, the class FancyArrow in matplotlib has Patch as base class and instance variables head_length and head_width. So I would getNamesOfInstanceVariables(FancyArrow) to return ['head_length','head_width', *listOfInstanceVarsForPatch].
EDIT
Let me add a bit of background on why I am asking for a dynamical way to write these functions. Let's say I have finished my script and it includes the classes Window, Door, Wall and many class compositions of these three. One of these many class compositions is Room. This class Room has ten hardcoded set_ functions that look like this:
def set_windowcolor(r, color):
r.window.color = color
return r
I now decide that I want to add another instance variable to the class Window. For example,
class Window:
def __init__(self, color='white', height=1.0, width=0.8, open=False):
self.color = color
self.height = height
self.width = width
self.open = open # new attribute of Window
Similar to all the other instance variables of window, this new attribute of Window should be customizable in all classe compositions that contain a Window. So I would go through my code, find the class Room and add a function
def set_windowopen(r, open):
r.window.open = open
return r
I would also have to look for all other class compositions that contain a Window and update them manually as well. I don't want to do this because it is a lot of work and I am likely going to overlook some class dependencies in the process and introduce bugs into my code. I am looking for a solution that either
generates set functions for single properties (e.g. set_windowcolor) automatically in Room for all instance variables of Window or
automatically adjusts the list of keyword arguments in set_windowprops or set_props.
Here is what I would do
class Room:
def __init__(self, kw_door=None, kw_window=None, kw_wall=None):
if kw_door:
self.door = Door(**kw_door)
else:
self.door = Door()
if kw_window:
self.window = Window(**kw_window)
else:
self.window = Window()
if kw_wall:
self.wall = Wall(**kw_wall)
else:
self.wall = Wall()
effectively you are accepting a dictionary that will be unpacked into the instance creation, and when the class definition gets new attributes, they too will be unpacked if they are found in the passed dictionary.
To answer you first question. If you want to set window attribute of r and kw attribute of that `windows as in your example, you cat do the following:
setattr(getattr(r, "window"), kw, kwargs[kw])
You get attribute named window of r. And for that windows attributes set its own whose name is in variable kw to a new value from kwargs[kw]. As your line has attempted.
But to be perfectly honest, why a function with many possible arguments would be preferred over just accessing/setting the attributes themselves? Or in other words, on the face of it, it looks more complicated then it needs to be.
As for you second question. You should be also able to just use dir() to get list of instance attributes, but yes, inherited attributes will be getting in your way and I am not immediately sure if there is an elegant way around it (i.e. not walk the inheritance tree and do the pruning yourself). But if you genuinely wanted to dynamically walk attributes of an instance that are subject to for instance setting through a function, I'd say add a (class) attribute defining what those are to be for each type rely on that as a defined interface.
side note: There is only one instance of None (and True and False) and it can be tested for identity (not just equality) and since it reads a little better you would normally see if somevar is not None instead of if not somevar == None.

Changing class attributes by reference

I'm relatively new to Python and have problems with immutable variables.
I'm trying to change the value of a class attribute (e.g. car.color). The difficulty is, that I can not use the namespace of car for doing this.
Up to now I did not find a satisvying answer to my questions. In the code below I tried to summarize the possible solutions (workarrounds) I found and their disadvantages:
class Car:
def __init__(self):
self.color = "green"
self.color_list = ["green"]
self.color_attrib = "green"
self.name = "VW Golf"
"""
and many more attributes...
"""
def makesetter(self, attribute):
def set_value(value):
attribute=value
return set_value
def set_color(self, value):
"in this function I directly have access to car.color and can change its value: "
self.color = value
def set_attrib(self, attribute_string, value):
setattr(self,attribute_string,value)
def change_attribute(attribute, value):
"In this function I can not access car.color directly"
attribute=value
def change_attribute_list(attribute, value):
"In this function I can not access car.color directly"
attribute[0] = value
if __name__ == "__main__":
car1 = Car()
change_attribute(car1.color, "red")
print(car1.color) # Color does not change because car1.color is immutable
g = car1.makesetter(car1.color)
g("red")
print(car1.color) # Color does not change because car1.color is immutable
change_attribute_list(car1.color_list, "red")
print(car1.color_list) # Color changes but seems like a workarround
# Disadvantage: in the namespace of car1, the user has to use a list to access a string value "car1.color_list[0]"
car1.set_color("red")
print(car1.color) # Color changes but seems like a workarround
# Disadvantage: Car needs a setter function for each attribute
car1.set_attrib("color_attrib","red")
print(car1.color_attrib) # Color changes but seems like a workarround
# Disadvantage: Attribute has to be passed as string & no auto completion while coding
Actually the function setattr() is internally exactly doing what I want. But it works with a string argument.
I tried to look into this function but it seems to be written in C++.
So do I have to use C++ to solve this problem without a workarround?
Or is there a Pythionic way of doing this?
The problem is you are trying to redefine the value of an instance from outside of the class. Since in __init__ you are defining your variables with self, they are only available for that instance. This is the point of a class - it's what makes them extensible and reusable.
Ideally, you would make a method within the class that would update those attributes, however, if you really need to update the class from an external function, you will have to define it as a class level variable. For instance:
class Car:
def __init__(self):
Car.color = "green"
can now be updated using:
def change_attribute(attribute, value):
"In this function I can not access car.color directly"
Car.color=value
outside of the class because you have not assigned it to one specific instance. Doing this presents a problem, however. Since we don't have a separate instance variable, if we try to re-instantiate the class, we are stuck with what was previously changed, i.e.
if name == "main":
car1 = Car()
car2 = Car()
change_attribute(car1.color, "red")
print(car1.color) # Prints red
print(car2.color) # Prints red
change_attribute(car2.color, "blue")
print(car1.color) # Prints blue
print(car2.color) # Prints blue
This is why classes themselves should be self contained and are meant to be immutable - the instance itself should be changed.

Can't assign value to object's property

I have really weird problem. I wrote one class, then I wanted to modify her a bit. I changed few things and now:
class Window (Drawable):
__width = 0
def __init__(self, intext="", intitle="", inwidth=50, inheight=10, startpos=Position()):
self.__width = inwidth
print inwidth
print self.__width
I'm using this code to present my problem. Whenever I create an object by using this class, it prints 50 and then 0. This is quite weird, cause what I'm doing there is, I think, a basic way to change this value.
What i'm doing wrong?
For a while I thought it's due to this code
def __setattr__(self, key, value):
if key == "position" or key == "width" or key == "height":
if key == "position":
self.__position = value
if key == "width":
self.__width = value
if key == "height":
self.__height = value
self.__get_shape()
But I commented it and nothing changed.
Then I thought it's because variable couldn't be named with underscores, but this isn't true either.
I'm all out of ideas, really.
Edit:
Now I found the cause. It's, as I thought, setattr - parent class also uses one. Is there any way to make settattr works only for other classes, or only for other keys? I want it just to set position, height and width in my way.
Customising __setattr__ can be tricky, error-prone, and simply isn't the right tool for the job here.
To customise setters, it would be a better design decision to use python properties rather than muck around with __setattr__. Here is a simplified example:
class Window(object):
def __init__(self, width=640):
self._width = width
#property
def width(self):
"""I'm the 'width' property."""
return self._width
#width.setter
def width(self, value):
# your custom setter logic here...
self._width = value
Start reading here.

Python Classes: Variable subclass creation in the base class's methods

Here's the coding problem I am trying to solve... I have a base class, let's say Animal, and it has two subclasses, say Dog and Cat. My class Animal has a method, make_baby(), that both Dog and Cat will inherit. The trick I'm having trouble pulling off is that I want the return value to be a new instance of the subclass that called the function but with different attribute values, i.e. Dog.make_baby() should return a new Dog and Cat.make_baby() will return a new Cat.
I previously tried returning "type(self)()", but this is no good because type() return a type object, not a class.
Here is the full example code:
Class Animal():
def __init__(self, color):
self.color = color
def make_baby():
new_color = rand_color # a randomly chosen color
return #??? new class of the same type that called the method
Class Dog(Animal):
def pet():
print '*pant*'
Class Cat(Animal):
def pet():
print 'purrr'
So I'd like to avoid writing a make_baby() method for Dogs and Cats because the idea is that the method is exactly the same except for the returned class. I'd also like to avoid a bunch of if statements because I'd like to make and arbitrarily large number of subclasses to Animal.
You wrote:
this is no good because type() return a type object, not a class.
A type is a class, if you're using new-style classes. If you're using Python 3, you're set; all Python 3 classes are "new-style." If you're using Python 2.x, derive your class from object (or from something else that derives from object, like any built-in Python type).
But what you really want here is a class method, where you get a reference to the class passed in automatically.
class Animal(object):
def __init__(self, color):
self.color = color
#classmethod
def make_baby(cls):
return cls(rand_color) # randomly-chosen color
You can call it on the class (e.g. Animal.make_baby() or Dog.make_baby()) or on an instance; either way the method still receives the class as the first argument.
type() can be used to construct entirely new classes. What you want is:
class Animal():
def __init__(self, color):
self.color = color
def make_baby(self):
new_color = rand_color # a randomly chosen color
return self.__class__(new_color)
You approach will totally work! Just use new style classes.
Class Animal(object):
def __init__(self, color):
self.color = color
def make_baby(self):
new_color = rand_color # a randomly chosen color
return type(self)(new_color)
Class Dog(Animal):
def pet():
print '*pant*'
Class Cat(Animal):
def pet():
print 'purrr'
However, if make_baby(self) is not relying on details of self, what you want is a class-wide factory method, like in #Kindall's answer.

Categories