Python: Why does class variable get assigned? - python

Here is my code:
class MyClass:
def __init__(self):
self.value = 0
def set_value(self, value):
self.value = 5
def get_value(self):
return self.value
value = print("Hello")
a = MyClass()
The output is:
Hello
What I do not understand is why print("Hello") gets executed. When I create an instance of the class only the instance variable is set to 0. Why self.value = 0 calls value = print("Hello")?
Can someone explain me this behavior?

The code evaluates the class when you execute it, and calls the print to define the class variable value.
The below example shows that it's printed before the instanciation.
class MyClass:
def __init__(self):
self.value = 0
def set_value(self, value):
self.value = 5
def get_value(self):
return self.value
value = print("Hello")
print('hi')
a = MyClass()
#output
>>> Hello
>>>hi

It doesn't. That print is executed because it's at the class level itself; the body of a class is executed at the time the class is defined. You would get that output even if you never instantiated MyClass.

Don't let the indentation trick you. value is not an instance variable. value is a class variable because it is defined in the class's scope. It's the same as doing:
class MyClass:
value = print("Hello")
....
Which means that the call to print will run at class definition time. In other words, when Python defines MyClass it also defines all of the class level variables - including value. To define value, it then calls print, which is why Hello is printed before you create an instance of MyClass.
If you want only instances of MyClass to print Hello, put the variable definition inside of the class constructor.
Side note: The print function returns None, so it's seems a bit strange that your assigning the value to a variable. Perhaps you were looking for something like input instead?

It does not, drop a = MyClass() and it will print "Hello" anyway. It executes code in the body when a class is defined:
class MyClass:
print(2 * 2)
# prints 4

Related

Python - Class object as function argument: Object only - Class not in argument

I am trying to write a function taking a string as an argument and using this argument as a class object.
Note that my explanantion might be strangely formulated sice I could not find an answer online. The MWE below should clarify what I mean, the problematic line is indicated.
Edit: in the MWE, "print" is an example. I need to be able to call the object to update it, print it or, in the case of a list, append to it. I need access to the object itself, not the value of the object.
MWE
# Create a class
class myClass():
def __init__(self):
self.one = "Test"
self.two = "Plop"
# Define function
def myFunction (parameter):
print(myObject.parameter)##### This line is currently not possible.
# Use class
myObject = myClass()
# Use function
myFunction("one")
I am not trying to append a new object to the class, only to call an existing object.
Is this even possible?
Looks like you need the built-in function called getattr
my_object = myClass()
def my_function(parameter):
print(getattr(my_object, parameter, None))
also this is not the best practice to call objects from outer scope like that. i'd suggest to use dict magic methods:
class MyClass:
def __init__(self):
self.one = "Test"
self.two = "Plop"
def __getitem__(self, parameter):
return getattr(self, parameter, None)
def __setitem__(self, parameter, value):
return setattr(self, parameter, value)
my_obj = MyClass()
parameter = "x"
print(my_obj[parameter])
my_obj[parameter] = "test"
print(my_obj.x)
You need to use getarttr():
# Create a class
class myClass():
def __init__(self):
self.one = "Test"
self.two = "Plop"
# Use class
myObject = myClass()
# Define function
def myFunction(parameter):
print(getattr(myObject, parameter))##### This line is currently possible.
# Use function
myFunction("one")

See function stop from locating defined class in Python

I'd like to define a class inside a function (for testing purpose) and put a value into
a function variable:
def foo():
myvar = None
class myclass:
def run(self):
myvar = 5
mm = myclass()
mm.run()
print(myvar)
The above prints None
Is there any way other than global to make the myvar variable accessible from the class? The correct answer would print 5
It's not possible to assign a value to a variable outside the current scope without global. If you need to persist the value within the class you can define class variables instead. Example:
def foo():
class Class:
var_to_change = None
def run (self):
self.var_to_change = 5
print (Class.var_to_change)
instance = Class()
instance.run()
print (Class.var_to_change)
I haven't tested the above code but it should work in theory.

How to override a method of a member object?

In python 3.4 I have a member object through composition.
I would like to override one of it's member functions.
def class Foo:
def __init__(self, value):
self.value = value
def member_obj.baz(baz_self, arg):
print("my new actions on {}".format(arg))
Foo.member_obj.baz(arg) #the original function
foo_inst = Foo(2)
bar = Bar(*bar_parameters) #from a third party module
setattr(foo_inst, "member_obj", bar) #it did not "stick" when I did foo_inst.member_obj = bar
foo_inst.member_obj.baz("some argument")
It does not make sense to inherit from the Bar class.
I also only want this different behaviour to occur if the object is inside Foo. I use Bar in many other places and would like to retain the same way of calling the method. I.e. I would like to avoid wrapping it in Foo.baz.
Is it even possible to do something like the def member_obj.baz and is it a good idea?
It would be similar to this: https://softwareengineering.stackexchange.com/questions/150973/what-are-the-alternatives-to-overriding-a-method-when-using-composition-instea
Are you trying to do something like this?
class B():
def __init__(self):
self.x = None
def fun(self):
print("Assigning value to attribute of object of class B.\n")
self.x = "Value of B object's attribute"
class A():
def __init__(self):
self.value = B()
def fun(self):
print("Screw this, I'll do something else this time!\n")
self.value.x = 13
def override(self):
# Edit: you can assign any identifier (that is not reserved) to
# any type of object or method AND the "fun" ("really self.fun")
# above is visible from here, since we passed "self" as an
# argument
self.value.fun = self.fun
myObj = B()
myOtherObj = A()
myOtherObj.override()
myObj.fun()
myOtherObj.value.fun()

How to print a variable from a derived class?

I know there are similar questions on this topics but none of them seem to apply to my case
Why would the following code print None and not True?
Thanks
class A(object):
flag = None
#classmethod
def set_flag(cls):
cls.flag = True
class B(A):
#classmethod
def print_super_flag(cls):
print cls.__bases__[0].flag # This prints None
print super(B, cls).flag # This also
if __name__ == "__main__":
b = B()
b.set_flag()
b.print_super_flag()
This call:
b.set_flag()
You defined set_flag as:
#classmethod
def set_flag(cls):
cls.flag = True
So when b calls set_flag, cls is B - so it sets B.flag, not A.flag. Then when you go to print A.flag, it is None because you never touched it. So your issue is not at all in print_super_flag - you access A.flag correctly there. Twice.
If instead you defined set_flag as:
class A(object):
flag = None
#classmethod
def set_flag(cls):
A.flag = True
Your code prints out True (twice), because set_flag now sets A.flag in derived classes. Of course at this point your method being a classmethod is moot, but that's a function of how you expect the method to perform; when you call a classmethod from a derived class, cls is that derived class, not the parent class.
Try this slightly extended version to see what's going on:
class A(object):
flag = None
#classmethod
def set_flag(cls):
cls.flag = True
class B(A):
#classmethod
def print_super_flag(cls):
print cls.__bases__[0].flag # This prints None
print super(B, cls).flag # This also
if __name__ == "__main__":
b = B()
b.set_flag()
print b.flag
b2 = B()
print b2.flag
a = A()
print a.flag
Prints:
True # because you set b.flag
True # because b.flag and b2.flag refer to the same class variable, B.flag
None # because B.flag and A.flag are NOT the same class variable
Basically, the child class (B) has its own copy of the flag class variable, rather than sharing it with the parent class (A).
More details here, assuming you're using Python 2.7-ish.
If you only want there to only one flag attribute that's part of the base class, you need to change the set_flag() method to:
class A(object):
flag = None
#classmethod
def set_flag(cls):
A.flag = True
Since the revised method doesn't reference its cls argument, you could just make it staticmethod and remove the argument, although that's not extremely important. Regardless, by hardcoding the base class, you ensure which flag attribute is being set, otherwise another flag attribute will be created as part of cls argument, which is the class the call was made through.
Since only one class attribute exists, its value will be printed.

Is it safe to replace a self object by another object of the same type in a method?

I would like to replace an object instance by another instance inside a method like this:
class A:
def method1(self):
self = func(self)
The object is retrieved from a database.
It is unlikely that replacing the 'self' variable will accomplish whatever you're trying to do, that couldn't just be accomplished by storing the result of func(self) in a different variable. 'self' is effectively a local variable only defined for the duration of the method call, used to pass in the instance of the class which is being operated upon. Replacing self will not actually replace references to the original instance of the class held by other objects, nor will it create a lasting reference to the new instance which was assigned to it.
As far as I understand, If you are trying to replace the current object with another object of same type (assuming func won't change the object type) from an member function. I think this will achieve that:
class A:
def method1(self):
newObj = func(self)
self.__dict__.update(newObj.__dict__)
It is not a direct answer to the question, but in the posts below there's a solution for what amirouche tried to do:
Python object conversion
Can I dynamically convert an instance of one class to another?
And here's working code sample (Python 3.2.5).
class Men:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a men! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_men(self):
print('I made The Matrix')
class Women:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a women! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_women(self):
print('I made Cloud Atlas')
men = Men('Larry')
men.who_are_you()
#>>> I'm a men! My name is Larry
men.method_unique_to_men()
#>>> I made The Matrix
men.cast_to(Women, 'Lana')
men.who_are_you()
#>>> I'm a women! My name is Lana
men.method_unique_to_women()
#>>> I made Cloud Atlas
Note the self.__class__ and not self.__class__.__name__. I.e. this technique not only replaces class name, but actually converts an instance of a class (at least both of them have same id()). Also, 1) I don't know whether it is "safe to replace a self object by another object of the same type in [an object own] method"; 2) it works with different types of objects, not only with ones that are of the same type; 3) it works not exactly like amirouche wanted: you can't init class like Class(args), only Class() (I'm not a pro and can't answer why it's like this).
Yes, all that will happen is that you won't be able to reference the current instance of your class A (unless you set another variable to self before you change it.) I wouldn't recommend it though, it makes for less readable code.
Note that you're only changing a variable, just like any other. Doing self = 123 is the same as doing abc = 123. self is only a reference to the current instance within the method. You can't change your instance by setting self.
What func(self) should do is to change the variables of your instance:
def func(obj):
obj.var_a = 123
obj.var_b = 'abc'
Then do this:
class A:
def method1(self):
func(self) # No need to assign self here
In many cases, a good way to achieve what you want is to call __init__ again. For example:
class MyList(list):
def trim(self,n):
self.__init__(self[:-n])
x = MyList([1,2,3,4])
x.trim(2)
assert type(x) == MyList
assert x == [1,2]
Note that this comes with a few assumptions such as the all that you want to change about the object being set in __init__. Also beware that this could cause problems with inheriting classes that redefine __init__ in an incompatible manner.
Yes, there is nothing wrong with this. Haters gonna hate. (Looking at you Pycharm with your in most cases imaginable, there's no point in such reassignment and it indicates an error).
A situation where you could do this is:
some_method(self, ...):
...
if(some_condition):
self = self.some_other_method()
...
return ...
Sure, you could start the method body by reassigning self to some other variable, but if you wouldn't normally do that with other parametres, why do it with self?
One can use the self assignment in a method, to change the class of instance to a derived class.
Of course one could assign it to a new object, but then the use of the new object ripples through the rest of code in the method. Reassiging it to self, leaves the rest of the method untouched.
class aclass:
def methodA(self):
...
if condition:
self = replace_by_derived(self)
# self is now referencing to an instance of a derived class
# with probably the same values for its data attributes
# all code here remains untouched
...
self.methodB() # calls the methodB of derivedclass is condition is True
...
def methodB(self):
# methodB of class aclass
...
class derivedclass(aclass):
def methodB(self):
#methodB of class derivedclass
...
But apart from such a special use case, I don't see any advantages to replace self.
You can make the instance a singleton element of the class
and mark the methods with #classmethod.
from enum import IntEnum
from collections import namedtuple
class kind(IntEnum):
circle = 1
square = 2
def attr(y): return [getattr(y, x) for x in 'k l b u r'.split()]
class Shape(namedtuple('Shape', 'k,l,b,u,r')):
self = None
#classmethod
def __repr__(cls):
return "<Shape({},{},{},{},{}) object at {}>".format(
*(attr(cls.self)+[id(cls.self)]))
#classmethod
def transform(cls, func):
cls.self = cls.self._replace(**func(cls.self))
Shape.self = Shape(k=1, l=2, b=3, u=4, r=5)
s = Shape.self
def nextkind(self):
return {'k': self.k+1}
print(repr(s)) # <Shape(1,2,3,4,5) object at 139766656561792>
s.transform(nextkind)
print(repr(s)) # <Shape(2,2,3,4,5) object at 139766656561888>

Categories

Resources