Adding a method to metaclass works perfectly in the below example.
class Test(object):
def __init__(self, x):
self.x = x
def double(self):
return self.x*2
# method to add
def quadruple(self):
return self.x*4
# creating metaclass
TypeTest = type('TypeTest', (Test,), {'triple': triple,
'quadruple': quadruple})
# prints 8
TypeTest(2).quadruple()
The below example doesn't work and I have no idea why. It simply doesn't recognise self in the parsed function and a TypeError occurs.
class Vehicle(object):
def __init__(self, wheels, door=False):
self.wheels = wheels
self.door = door
# method to add
def check_load(self, x):
if x > self.load:
return "Load won't fit"
else:
return "Load will fit"
# creating metaclass
Truck = type('Truck', (Vehicle,), dict(wheels=4,door=True, load=100,
check_load=check_load))
# TypeError: check_load() missing 1 required positional argument: 'x'
Truck.check_load(10)
First of all: You are not creating a metaclass, you are creating regular classes. type() is the (base) metaclass here, calling it creates a new class object (the same type of object that a class statement produces).
The first type() call is essentially equivalent to:
class TypeTest(Test)
triple = triple
quadruple = quadruple
and the second example is the same as:
class Truck(Vehicle)
wheels = 4
door = True
load = 100
check_load = check_load
You forgot to create an instance of your Truck class:
Truck.check_load(10)
This leaves the check_load() function with nothing to bind to, there is no self.
In your first example you did create an instance:
TypeTest(2).quadruple()
Notice the call, passing in 2.
Create an instance for self to be bound to:
Truck(4, True).check_load(10)
If you wanted your class to not need arguments to create an instance, you'll need to provide a different __init__ method too, one that overrides the Vehicle.__init__ method:
def init(self): pass
Truck = type('Truck', (Vehicle,), dict(
wheels=4,door=True, load=100,
check_load=check_load, __init__=init))
Now you can create the instance without arguments:
Truck().check_load(10)
Related
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>
I have a class in python with a function and I need that function to explicitly return an instance of that class. I tried this
class a(type):
def __init__(self, n):
self.n = n
def foo() -> a:
return a(self.n + 1)
but I get an error "a is not defined". What should I do? Thanks.
Since OP used annotation in member function. There is a NameError in the annotation also. To fix that. Try following:
Reference:
https://www.python.org/dev/peps/pep-0484/#id34
Annotating instance and class methods
In most cases the first argument of class and instance methods does
not need to be annotated, and it is assumed to have the type of the
containing class for instance methods, and a type object type
corresponding to the containing class object for class methods. In
addition, the first argument in an instance method can be annotated
with a type variable. In this case the return type may use the same
type variable, thus making that method a generic function.
from typing import TypeVar
T = TypeVar('T', bound='a')
class a:
def __init__(self: T, n: int):
self.n = n
def foo(self: T) -> T:
return a(self.n + 1)
print(a(1).foo().n)
Result:
2
What you are asking works:
class A:
def __init__(self, n):
self.n = n
def foo(self):
return A(self.n + 1)
a = A(1)
b = a.foo()
print(a.n, b.n)
There are sevaral problems with your original code though.
The type hint -> A does not work because A is not defined at that point.
You need to pass self to the foo method as well.
If you subclass type, and want to make use of its features, I suggest you also initialize it by calling super().__init__() and pass on all necessary arguments. You can do that at any point you prefer, but usually it's done in the __init__() method of the subclass.
I have a class that I need:
First instance MUST receive a parameter.
All the following instances have this parameter be optional.
If it is not passed then I will use the parameter of the previous object init.
For that, I need to share a variable between the objects (all objects belong to classes with the same parent).
For example:
class MyClass:
shared_variable = None
def __init__(self, paremeter_optional=None):
if paremeter_optional is None: # Parameter optional not given
if self.shared_variable is None:
print("Error! First intance must have the parameter")
sys.exit(-1)
else:
paremeter_optional = self.shared_variable # Use last parameter
self.shared_variable = paremeter_optional # Save it for next object
objA = MyClass(3)
objB = MyClass()
Because the shared_variable is not consistent/shared across inits, when running the above code I get the error:
Error! First intance must have the parameter
(After the second init of objB)
Of course, I could use a global variable but I want to avoid it if possible and use some best practices for this.
Update: Having misunderstood the original problem, I would still recommend being explicit, rather than having the class track information better tracked outside the class.
class MyClass:
def __init__(self, parameter):
...
objA = MyClass(3)
objB = MyClass(4)
objC = MyClass(5)
objD = MyClass(5) # Be explicit; don't "remember" what was used for objC
If objC and objD are "related" enough that objD can rely on the initialization of objC, and you want to be DRY, use something like
objC, objD = [MyClass(5) for _ in range(2)]
Original answer:
I wouldn't make this something you set from an instance at all; it's a class attribute, and so should be set at the class level only.
class MyClass:
shared_variable = None
def __init__(self):
if self.shared_variable is None:
raise RuntimeError("shared_variable must be set before instantiating")
...
MyClass.shared_variable = 3
objA = MyClass()
objB = MyClass()
Assigning a value to self.shared_variable makes self.shared_variable an instance attribute so that the value is not shared among instances.
You can instead assign the value explicitly to the class attribute by referencing the attribute of the instance's class object instead.
Change:
self.shared_variable = paremeter_optional
to:
self.__class__.shared_variable = paremeter_optional
I have one class with constructor that recieve two functions as callback's.
class TreeTraverse:
def __init__(self, branch_head: Node, node_processor: Callable[[Phrase], None], ascent: Callable[[], None]):
self._branch_head: Node = branch_head
self._ascent: Callable[[], None] = ascent
self._node_processor: Callable[[Phrase], None] = node_processor
...
I annotated parameters using typing module: one of them should recieve 1 parameter, other - without parameters. Then I have another class which passes their methods as callbacks:
class CodeGenerator:
def phrase_processor(self, phrase: Phrase):
pass
def asc(self):
pass
def generate(self):
tree_traverse = TreeTraverse(self._tree.get_head(), node_processor=self.phrase_processor, ascent=self.asc)
tree_traverse.traverse()
The question is why this code works? Is it allowed to be left as is?
Class method has extra "self" argument at the first position. And at the place where function called I should pass class instance to this method. But appears that is not required.
My guess is that the all in python is object (including functions and methods), and this object has a stash, where self is put when I pass method.
I have something like this:
class exampleClass(object):
def doSomething(self,number):
return number + 1
class exampleClass2(exampleClass):
def callDefDoSomething(self):
print exampleClass.doSomething(5)
exampleClass2.callDefDoSomething()
-
TypeError: unbound method callDefDoSomething() must be called
with exampleClass2 instance as first argument (got nothing instead)
I started to learn about objects in Python but i cant find solution for this :(
You need to create an instance of the class, i.e., an active object, to make things work:
class exampleClass(object):
def doSomething(self,number):
return number + 1
class exampleClass2(exampleClass):
def __init__(self):
self.member1 = exampleClass()
def callDefDoSomething(self):
print self.member1.doSomething(5)
object2 = exampleClass2()
object2.callDefDoSomething()
doSomething is a method of exampleClass. Therefore, it has to be called for an instance of this class.
In callDefDoSomething, you use
exampleClass.doSomething(5)
exampleClass, however, is not an instance of this class but the class itself. What you want to use here is
self.doSomething(5)
self refers to the instance of exampleClass2, for whichcallDefDoSomethingsis invoked, which, due to inheritance, is an instance ofexampleClass`.
Regular class methods can only be called for instances not for classes. So if you want to call callDefDoSomething you have to first instantiate exampleClass2. You also have to instantiate exampleClass inside the call to callDefDoSomething.
class exampleClass(object):
def doSomething(self,number):
return number + 1
class exampleClass2(exampleClass):
def callDefDoSomething(self):
exampleClassInstance = exampleClass()
print exampleClassInstance.doSomething(5)
exampleClass2Instance = exampleClass2()
exampleClass2Instance.callDefDoSomething()
If you want to call methods on classes you should try classmethods. Check the documentation on classes in the python tutorial.
You can use this:
class exampleClass(object):
def doSomething(self,number):
return number + 1
class exampleClass2(exampleClass):
def callDefDoSomething(self):
print super(exampleClass2,self).doSomething(5)
example = exampleClass2()
example.callDefDoSomething()