Cannot mutate default parameter list of derived class - python

The is a minimized script I have:
import random
def genvalue():
return random.randint(1, 100)
class A(object):
def __init__(self, x = genvalue()):
self.x = x
class B(A):
def __init__(self):
super(B, self).__init__()
t1 = A(10)
t2 = B()
t3 = B()
print t1.x
print t2.x
print t3.x
The expected result I wanted is t1.x having the value 10, and the other two having random values, but instead both t2 and t3 have the same values, it is like the genfunc is only called once. I would like to have it called each time when an instance is initiated. Is it possible to do it without messing with the function signatures?

Default arguments are evaluated at callable creation time.
Currently, genvalue is called exactly once in your program, at the time the method __init__ is being being built in order to bind the default value of x to the method.
Demo:
import random
def genvalue():
print('genvalue called')
return random.randint(1, 100)
class A(object):
def __init__(self, x=genvalue()):
self.x = x
print('creating some instances...')
A()
A()
A()
print(A.__init__.__defaults__)
Output:
genvalue called
creating some instances...
(32,)
Use
class A(object):
def __init__(self, x=None):
self.x = x if x is not None else genvalue()

genfunc is only called once - when the class description is read for the first time. The parameter is evaluated there - it's not evaluated each time the class is created.
Instead set the default value to None, and if a value isn't given, generated it in your __init__ method instead.
class A(object):
def __init__(self, x=None):
if x is None:
x = genvalue()
self.x = x

Related

Can the inherited class see the change of the value on the parent class?

I have two classes. class A has a public variable X, which is used by both classes,
class A changes the value of X every 3 seconds, while class B prints the new value of X. But the class B sees the initial value 10 only. I need class B to see the change of the variable X in Class A.
import threading
import time
class A():
X = 10
def __init__(self):
self.first()
def first(self):
while True:
self.X = self.X + 3
print("A",self.X)
time.sleep(3)
class B(A):
def __init__(self):
t = threading.Thread(target= self.second, args=())
t.setDaemon(True)
t.start()
def second(self):
while True:
print(self.X)
time.sleep(3)
example1 = B()
example2 = A()
This is not setting the class variable X.
self.X = self.X + 3
On the first iteration self.X is reading the class variable since there is no instance variable X.
However it assigns the instance variable and from that point on, self.X within A is an instance variable and any changes made are not reflected in A.X.
You can fix this by making the first argument of the method (self) refer to the class and not the instance with the #classmethod decorator.
#classmethod
def first(cls):
while True:
cls.X = cls.X + 3
print("A",cls.X)
time.sleep(3)
Full code:
import threading
import time
class A():
X = 10
def __init__(self):
self.first()
#classmethod
def first(cls):
while True:
cls.X = cls.X + 3
print("A",cls.X)
time.sleep(3)
class B(A):
def __init__(self):
t = threading.Thread(target= self.second, args=())
t.setDaemon(True)
t.start()
def second(self):
while True:
print(self.X)
time.sleep(3)
example1 = B()
example2 = A()
You can access its parent's X, A's x, by either using: A.x or super().x
It doesn't work the way you think it should work. The objects A() and B() are not the same as classes A and B. The objects are separated instances. When you change x using object A(), object B() doesn't know anything about the change that happened. And it couldn't.
If you need it to work the way you want, you should add A() into b's initializer. And then call the variable of A() object.
class B:
def __init__(self, A_instance):
self.a = A_instance
def working(self):
x = self.a.x

Define partial specialization of Class

I have a bunch of classes that all vary only by a string and a function, like so:
class ClassOne:
__init__(self, x):
self._x = x
self._my_str = 'hello'
def greet():
fun1(self._x)
return self._my_str
class ClassTwo:
__init__(self, x):
self._x = x
self._my_str = 'howdy'
def greet():
fun2(self._x)
return self._my_str
I would love to be able to define
ClassOne = ClassTemplate('hello', fun1)
ClassTwo = ClassTemplate('howdy', fun2)
So that ClassOne and ClassTwo still act as normal classes, i.e. can be imported into other modules and the likes. I realise this is probably a standard technique, but not knowing its name I cannot manage to Google it (that also explains my inability to properly name the question)
I realise I can take them in as part of the init, but the choice of function and string is not obvious for the user at all, so I would prefer to name them.
Edit: It seems people misunderstand what I seek. Continuing the example I want to be able to do
first_instance = ClassOne(3.14)
second_instance = ClassTwo(2.71)
first_instance.greet()
I.e. ClassOne and ClassTwo need to be proper class definitions.
_my_str can be class attribute, which you can set using
__init_subclass__ provided by an appropriate base class. We'll make fun a static method so that the user doesn't have to define fun specially.
class BaseClass:
def __init_subclass__(cls, my_str=None, fun=None):
cls._my_str = my_str
cls._fun = staticmethod(fun)
def __init__(self, x):
self._x = x
def greet(self):
self._fun(self._x)
return self._my_str
class ClassOne(BaseClass, my_str='hello', fun=fun1):
pass
class ClassTwo(BaseClass, my_str='howdy', fun=fun2):
pass
(If you just used cls._fun = fun, then self._fun(self._x) would be equivalent to type(self)._fun(self, self._x), rather than the intended type(self)._fun(self._x).)
Alternately, you can simply declare the class attributes explicitly, though now the caller is responsible for correctly defining fun as a static method.
class BaseClass:
def __init__(self, x):
self._x = x
def greet(self):
self._fun(self._x)
return self._my_str
class ClassOne(BaseClass):
_my_str = 'hello'
fun = staticmethod(fun1)
class ClassTwo(BaseClass):
my_str = 'howdy'
fun = staticmethod(fun2)
In either case, you can defer setting the class attributes:
class ClassThree(BaseClass):
pass
# time passes
ClassThree._my_str = "g'day"
ClassThree.fun = staticmethod(fun3)
You can make the classes take in parameters when creating them
Update:
To name parameters, set a default value
class ClassThing:
def __init__(self, x, string=None, function=None):
self.x = x #whatever x is
self.string = string
self.function = function
def greet(self):
self.function(self.x)
return self.string
Then you can do
>>> classthing1 = ClassThing(32, string = "hello1", function = lambda x: print(f"LOL {x}"))
>>> classthing2 = ClassThing(129, string = "hello2", function = lambda x: print(f"LOL lmao {x}"))
>>> classthing1.greet()
LOL hello1
32
>>> classthing2.greet()
LOL lmao hello2
129
Edited because I was way off
As you don't want to pass the function in the init you can do this
class BaseClass:
def __init__(self, x):
self._x = x
self._my_str = 'hello'
self.fun = None
def greet(self):
self.fun(self._x)
return self._my_str
def fun(x):
print(x)
def ClassTemplate(s, f):
res = BaseClass(s)
res.fun = f
return resr
a = ClassTemplate('toto', fun)
a.greet()

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()

How to make local variable of a method of Superclass available to a subclass where the superclass is called by another class

As you can see the code, I have a super class bar_for_foo_mixin() and I have a subclass myfoo(bar_for_foo_mixin): I am computing a operation self.Z = X+Y in bar() method of superclass.
Now I want the self.z = 0 updated to the computation done in bar() method and inheirt this value to the subclass myfoo(bar_for_foo_mixin): and use it inside subclass.
class bar_for_foo_mixin():
def __init__(self):
self.z = 0
def bar(self, q):
x = 2
y = 8
self.z = x + y + q
class oldfoo():
def __init__(self):
pass
var = bar_for_foo_mixin()
var.bar(10)
class myfoo(bar_for_foo_mixin):
def __init__(self):
super(myfoo, self).__init__()
def hello(self):
print("hello", self.z)
final = myfoo()
final.hello()
Result of the code:
hello 0
Expected result:
hello 20
The bar_for_foo_mixin instance stored in your oldfoo.var class variable is a completely separate instance from the myfoo object you instantiated in the main program, so their instance variable z would not be shared.
If you would like a variable to be shared across all instances of a class, you should make it a class variable instead, and make methods that are dedicated to updating class variables, such as bar_for_foo_mixin.bar, a class method instead:
class bar_for_foo_mixin():
z = 0
#classmethod
def bar(cls, q):
x = 2
y = 8
cls.z = x + y + q
class oldfoo():
def __init__(self):
pass
var = bar_for_foo_mixin()
var.bar(10)
class myfoo(bar_for_foo_mixin):
def __init__(self):
super(myfoo, self).__init__()
def hello(self):
print("hello", self.z)
final = myfoo()
final.hello()
This outputs:
hello 20
You're not even calling the bar method by the new final variable:
class bar_for_foo_mixin():
def __init__(self):
self.z = 0
def bar(self, q):
x = 2
y = 8
self.z = x + y + q
class myfoo(bar_for_foo_mixin):
def __init__(self):
super(myfoo, self).__init__()
def hello(self):
print("hello", self.z)
final = myfoo()
final.bar(10) # <== call it to take effect
final.hello() # ==> hello 20

Python: avoid defining both a classmethod and an instancemethod

NOTE on the question below. I think the 'proper' pythonic idiom is to a) create module functions, such as foo_math below, and then call their specific action against an instance within the class itself. The bottom piece of code reflects that approach.
I want to define a classmethod which takes two arguments and returns a value. I want the same method to be able to be called on a class instance with the instance value pass as one of the arguments. Can I do this without defining two distinct methods as I have done here?
class Foo(object):
__init__(x):
self.x = x
#classmethod
def foo_math(cls, x, y):
return x + y
def math(self, y):
return Foo.foo_math(self.x, y)
What I would like is:
>>> Foo.math(3, 4)
7
>>> f = Foo()
>>> f.x = 3
>>> f.math(4)
7
Short of subtyping int, here is my conclusion to this question:
def foo_math(cls, x, y):
return x + y
class Foo(object):
__init__(x):
self.x = x
def foo_math(self, y):
return foo_math(self, y)
i don't recommend doing this, but if you really want, it's this (thank you other guy on stackoverflow for first part):
class staticorinstancemethod(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return functools.partial(self.func, instance)
then, do something like
class F(object):
#staticorinstancemethod
def math(instOrNone, v1, v2=None):
return instOrNone.x + v1 if instOrNone else v1 + v2
but maybe you just want to define the __add__ and __radd__ methods...
I don't think that you can call a method from a class without defining an object of that class (class methods don't belong inside the methods of any one class), so things like Foo.math(3, 4) will return a NameError as Foo has not been defined.
With this in mind, you should modify your code to be like this (even though with the problem solved there are still some issues with the code):
# A class method would probably go here somewhere.
class Foo(object):
def __init__(self, x):
self.x = x
def foo_math(self, x, y):
return x + y
def math(self, y):
return self.foo_math(self.x, y)
Then you can do:
>>> f = Foo(3)
>>> f.math(4)
7

Categories