Class-variables access in super method of Python - python

Assume, I have similar code:
class Parent:
CONST_VAL = '1'
def calc(self):
return self.CONST_VAL
class Child(Parent):
CONST_VAL = 'child'
def calc(self):
return super().calc() + self.CONST_VAL
And then I execute the following:
c = Child()
c.calc()
>> childchild
I expect it to be '1child', but it's not.
Is there a right way to make a class-variable isolation of super methods like in C++?

In both of your methods you use self.CONST_VAL - this is an instance variable.
When you call super().calc() you indeed call method calc() of Parent class, but it also returns instance variable, which is the same, obviously.
What you might want to do is to use Parent.CONST_VAL in you parent class.

Related

Is there a __repr__() like method for a python class?

I'm solving a funny problem that requires to define a class that can be called like this:
class Chain(2)(3)(4)
And it should print out the multiplication of all arguments.
I ended up a solution like this:
class Chain():
calc = 1
def __new__(cls, a=None):
if a:
cls.calc = cls.calc*a
return cls
else:
return cls.calc
This works fine and self.calc is equal to 24 but i have a wrong representation <class '__main__.Chain'>.
Is there anyway to have representation of multiplication instead of class name like what we have in __repr__ for objects?
note: The call arguments count has no limits and may be different on each call.
First of all to answer your direct question from the title:
As everything in Python, classes are too - objects. And just like classes define how instances are created (what attributes and methods they will have), metaclasses define how classes are created. So let's create a metaclass:
class Meta(type):
def __repr__(self):
return str(self.calc)
class Chain(metaclass=Meta):
calc = 1
def __new__(cls, a=None):
if a:
cls.calc = cls.calc*a
return cls
else:
return cls.calc
print(Chain(2)(3)(4))
This will print, as expected, 24.
A few notes:
Currently Meta simply accesses a calc attribute blindly. A check that it actually exists could be done but the code above was just to make the point.
The way your class is implemented, you can just do Chain(2)(3)(4)() and you will get the same result (that's based on the else part of your __new__).
That's a weird way to implement such behavior - you are returning the class itself (or an int...) from the __new__ method which should return a new object of this class. This is problematic design. A classic way to do what you want is by making the objects callable:
class Chain():
def __init__(self, a=1):
self.calc = a
def __call__(self, a=None):
if a:
return self.__class__(self.calc * a)
else:
return self.calc
def __repr__(self):
return str(self.calc)
print(Chain(2)(3)(4))
This solves your problem of even needing to do what you want, because now you just implement the class' __repr__ (because now each call in the chain returns a new object, and not the class itself).

How can I use a child class' function attribute in the base class?

Overview
I have a python class inheritance structure in which most methods are defined in the base class and most attributes on which those methods rely are defined in child classes.
The base class looks roughly like this:
class Base(object):
__metaclass__ = ABCMeta
#abstractproperty
def property1(self):
pass
#abstractproperty
def property2(self):
pass
def method1(self):
print(self.property1)
def method2(self, val):
return self.property2(val)
while the child class looks like this:
class Child(Base):
property1 = 'text'
property2 = function
where function is a function that looks like this:
def function(val):
return val + 1
Obviously the code above is missing details, but the structure mirrors that of my real code.
The Problem
When I attempt to use method1 in the base class everything works as expected:
>>> child = Child()
>>> child.method1()
'text'
However, attempting the same for method2 spits an error:
>>> child = Child()
>>> child.method2(1) # expected 2
TypeError: method2() takes exactly 1 argument (2 given)
The second passed argument is the Child class itself.
I'm wondering if there's a way to avoid passing this second Child parameter when calling method2.
Attempts
One workaround I've found is to define an abstract method in the base class then build that function in the child classes like so:
class Base(object):
__metaclass__ = ABCMeta
#abstractproperty
def property1(self):
pass
#abstractmethod
def method2(self, val):
pass
def method1(self):
print(self.property1)
class Child(Base):
property1 = 'text'
def method2(self, val):
return function(val)
However, I would prefer that this method live in the base class. Any thoughts? Thanks in advance!
Methods implicitly receive self as the first argument, even if it seems that it is not passed. For example:
class C:
def f(self, x):
print(x)
C.f takes two arguments, but you'd normally call it with just one:
c = C()
c.f(1)
The way it is done is that when you access c.f a "bound" method is created which implicitly takes c as the first argument.
The same happens if you assign an external function to a class and use it as a method, as you did.
Solution 1
The usual way to implement a method in a child class is to do it explicitly there, rather than in an external function, so rather than what you did, I would do:
class Child(Base):
property1 = 'text'
# instead of: property2 = function
def property2(self, val):
return val + 1
Solution 2
If you really want to have property2 = function in the class (can't see why) and function out of the class, then you have to take care of self:
class Child(Base):
property1 = 'text'
property2 = function
def function(self, val):
return val + 1
Solution 3
If you want the previous solution, but without self in function:
class Child(Base):
property1 = 'text'
def property2(self, val):
return function(val)
def function(val):
return val + 1
Solution
Make your method static:
class Child(Base)
property2 = staticmethod(function)
Explanation
As zvone already explained, bound methods implicitly receive self as the first parameter.
To create a bound method you don't necessarily need to define it in the class body.
This:
def foo(self):
print("foo")
class Foo:
bar = foo
f = Foo()
print(f.bar)
will output:
>>> <bound method foo of <__main__.Foo object at 0x014EC790>>
A function assigned to a class attribute will therefore behave just as a normal class method, meaning that if you call it as f.bar() it is treated as a bound method and self is implicitly passed as first parameter.
To control what is and what is not implicitly passed to a class method as first argument is normally controlled with the decorators
#classmethod: the class itself is passed as the first argument
#staticmethod: no arguments are implicitly passed to the method
So you want the behavior of a staticmethod, but since you are simply assigning a already defined function to a class attribute you cannot use the decorator syntax.
But since decorators are just normal functions which take a function as parameter and return a wrapped function, this:
class Child(Base):
property2 = staticmethod(function)
is equivalent (*) to this:
class Child(Base):
#staticmethod
def property2():
function()
Further improvements
I would suggest a small additional modification to your Base class:
Rename property2 and mark it not as abstractproperty but as abstractstaticmethod(**).
This will help colleagues (and eventually yourself) to understand better what kind of implementation is expected in the child class.
class Base(object):
__metaclass__ = ABCMeta
#abstractstaticmethod
def staticmethod1(self):
pass
(*) well, more or less. The former actually assigns function to property2, the latter creates a new static method which delegates to function.
(**) abstractstaticmethod is deprecated since Python 3.3, but since you are also using abstractproperty I wanted to be consistent.

Classes returned from class factory have different IDs

I have a class factory method that is used to instantiate an object. With multiple objects are created through this method, I want to be able to compare the classes of the objects. When using isinstance, the comparison is False, as can be seen in the simple example below. Also running id(a.__class__) and id(b.__class__), gives different ids.
Is there a simple way of achieving this? I know that this does not exactly conform to duck-typing, however this is the easiest solution for the program I am writing.
def factory():
class MyClass(object):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
return MyClass()
a = factory()
b = factory()
print(a.compare(b))
The reason is that MyClass is created dynamically every time you run factory. If you print(id(MyClass)) inside factory you get different results:
>>> a = factory()
140465711359728
>>> b = factory()
140465712488632
This is because they are actually different classes, dynamically created and locally scoped at the time of the call.
One way to fix this is to return (or yield) multiple instances:
>>> def factory(n):
class MyClass(object):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
for i in range(n):
yield MyClass()
>>> a, b = factory(2)
>>> a.compare(b)
Comparison Result: True
is a possible implementation.
EDIT: If the instances are created dynamically, then the above solution is invalid. One way to do it is to create a superclass outside, then inside the factory function subclass from that superclass:
>>> class MyClass(object):
pass
>>> def factory():
class SubClass(MyClass):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
return SubClass()
However, this does not work because they are still different classes. So you need to change your comparison method to check against the first superclass:
isinstance(other, self.__class__.__mro__[1])
If your class definition is inside the factory function, than each instance of the class you create will be an instance of a separate class. That's because the class definition is a statement, that's executed just like any other assignment. The name and contents of the different classes will be the same, but their identities will be distinct.
I don't think there's any simple way to get around that without changing the structure of your code in some way. You've said that your actual factory function is a method of a class, which suggests that you might be able to move the class definition somewhere else so that it can be shared by multiple calls to the factory method. Depending on what information you expect the inner class to use from the outer class, you might define it at class level (so there'd be only one class definition used everywhere), or you could define it in another method, like __init__ (which would create a new inner class for every instance of the outer class).
Here's what that last approach might look like:
class Outer(object):
def __init__(self):
class Inner(object):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
self.Inner = Inner
def factory(self):
return self.Inner()
f = Outer()
a = f.factory()
b = f.factory()
print(a.compare(b)) # True
g = Outer() # create another instance of the outer class
c = g.factory()
print(a.compare(c)) # False
It's not entirely clear what you're asking. It seems to me you want a simpler version of the code you already posted. If that's incorrect, this answer is not relevant.
You can create classes dynamically by explicitly constructing a new instance of the type type.
def compare(self, other):
...
def factory():
return type("MyClass", (object,), { 'compare': compare }()
type takes three arguments: the name, the parents, and the predefined slots. So this will behave the same way as your previous code.
Working off the answer from #rassar, and adding some more detail to represent the actual implementation (e.g. the factory-method existing in a parent class), I have come up with a working example below.
From #rassar's answer, I realised that the class is dynamically created each time, and so defining it within the parent object (or even above that), means that it will be the same class definition each time it is called.
class Parent(object):
class MyClass(object):
def __init__(self, parent):
self.parent = parent
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
def factory(self):
return self.MyClass(self)
a = Parent()
b = a.factory()
c = a.factory()
b.compare(c)
print(id(b.__class__))
print(id(c.__class__))

Have a class to initialise an object from a different class [duplicate]

This question already has an answer here:
How to create child class instance using some magic in parent __new__?
(1 answer)
Closed 6 years ago.
Currently, I have a #classmethod that returns the right class given a variable. It looks something like:
class Parent():
#classmethod
def GetInstance(cls, variable):
if variable == 'foo':
return Child()
class Child(Parent):
def Something(self):
pass
instance = Parent.GetInstance('foo')
result = instance.Something()
I prefer not to define and use GetInstance. Instead, I would like the Main() to simply be:
instance = Parent('foo')
result = instance.Something()
While keeping all the benefits of the structure above. I want class Parent() to return an object of class Child() when called, without the need of using a method. __init__ unfortunately does not seem to help, as it cannot return anything. Any ideas?
Just override 'new' special method. It's 'new' method which is responsible for calling creating an instance, allocating memory and then initialize it by calling 'init' . To be specific
new is static class method, while init is instance method. new has to create the instance first, so init can initialize it. Note that init takes self as parameter. Until you create instance there is no self.
EDIT Ok, I went back and fixed it. Totally hackey and very brittle. I hope you don't want to keep extending this. I put this here as illustrative of why this is a bad idea.
Here is a sketch of how you could accomplish this. But please, just use a factory method.
>>> class Parent(object):
... def __new__(typ, *args, **kwargs):
... if args and args[0] == 'foo':
... return Child()
... else:
... return super(Parent, typ).__new__(typ)
...
>>> class Child(Parent):
... __new__ = object.__new__
...
>>> a = Parent('foo')
>>> a
<__main__.Child object at 0x1023275f8>
>>> p = Parent()
>>> p
<__main__.Parent object at 0x1023275c0>
>>>
Add a list of children[] in your parent class:
class Parent():
def __init__(self):
children = list()
That way you can create a child() object and then append it by doing:
child - Child() # create new child obj
parent.children.append(child)
Then it should be simple to get a child from the list of children that you want using a loop etc.

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