Creating multiple objects within the same class in Python - python

I want to be able to return multiple objects of a class by using a method within the class. Something like this.
class A:
def __init__(self,a):
self.a = a
def _multiple(self,*l):
obj = []
for i in l:
o = self.__init__(self,i)
obj.append(o)
return obj
When I execute this on iPython (iPython 0.10 and Python 2.6.6) I get the following
In [466]: l = [1,2]
In [502]: A._multiple(*l)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
TypeError: unbound method _multiple() must be called with A instance as
first argument (got int instance instead)
I'm definitely unclear about the invocation as well as the 'self' keyword usage. Could you help me out in getting this correct?
Thank you.

TypeError: unbound method _multiple() must be called with A instance
as first argument (got int instance instead)
The Error is self explanatory. It means that an instance method is being called as a Class method. To make the instance method as a class method add the decorator #classmethod
>>> class A:
def __init__(self,a):
self.a = a
#classmethod
def _multiple(cls,*l):
#Create multiple instances of object `A`
return [A(i) for i in l]
>>> l = [1,2]
>>> A._multiple(*l)
[<__main__.A instance at 0x066FBB20>, <__main__.A instance at 0x03D94580>]
>>>

You want a class method:
class A:
def __init__(self,a):
self.a = a
#classmethod
def _multiple(cls,*l):
obj = []
for i in l:
o = cls(i)
obj.append(o)
return obj
>>> A._multiple(1, 2) # returns 2 A's
[<__main__.A instance at 0x02B7EFA8>, <__main__.A instance at 0x02B7EFD0>]
The classmethod decorator replaces the usual self as the first parameter with a reference to the class (in this case A). Note that doing it this way means if you subclass A and call _multiple on the subclass it will be passed the subclass instead.
class B(A): pass
>>> B._multiple(1, 2, 3)
[<__main__.B instance at 0x02B87C10>, <__main__.B instance at 0x02B87D28>, <__main__.B instance at 0x02B87CD8>]
would create a list of B objects.

Simply replace:
self.__init__(self, i)
With:
A(i)
The reason for this is that the init method mutates the object on which it is called, and "self" is the current instance. You use the constructor (the same name as the class) to create a new instance.

Related

Create an object from a python function returns a class

I have a function which returns a class:
def my_function():
# some logic
class AClass:
def __init__(self, argument):
init()
return AClass
And when I call this function, it returns a class, not an object of that class, right?
Value = my_function()
My question is how can I create an object from that class AClass?
Thank you.
my_class = my_function()
my_obj = my_class(arg)
Since the method returns a reference to a type you can simply use whatever constructor that is defined for the class directly on the return value.
Take this class for example:
class A:
def __init_(self, n = 0):
self.__n = n
Lets see what happens when reference the type directly when running the interpreter interactively:
>>> A
<class `__main__.A`>
Now lets return the type in a method:
>>> def f():
>>> return A
>>> f()
<class `__main__.A`>
Since the value of referencing the class directly and when returned from a method is the same, you can use that returned value the same you would normally. Therefore a = A() is the same as a = f()(). Even if the class takes parameter you can still reference it directly: a = f()(n = 10)

Why doesn't super.__new__ need argument but instance.__new__ needs?

Trying to understand super and __new__
Here goes my code:
class Base(object):
def __new__(cls,foo):
if cls is Base:
if foo == 1:
# return Base.__new__(Child) complains not enough arguments
return Base.__new__(Child,foo)
if foo == 2:
# how does this work without giving foo?
return super(Base,cls).__new__(Child)
else:
return super(Base,cls).__new__(cls,foo)
def __init__(self,foo):
pass
class Child(Base):
def __init__(self,foo):
Base.__init__(self,foo)
a = Base(1) # returns instance of class Child
b = Base(2) # returns instance of class Child
c = Base(3) # returns instance of class Base
d = Child(1) # returns instance of class Child
Why doesn't super.__new__ need an argument while __new__ needs it?
Python: 2.7.11
super().__new__ is not the same function as Base.__new__. super().__new__ is object.__new__. object.__new__ doesn't require a foo argument, but Base.__new__ does.
>>> Base.__new__
<function Base.__new__ at 0x000002243340A730>
>>> super(Base, Base).__new__
<built-in method __new__ of type object at 0x00007FF87AD89EC0>
>>> object.__new__
<built-in method __new__ of type object at 0x00007FF87AD89EC0>
What may be confusing you is this line:
return super(Base,cls).__new__(cls, foo)
This calls object.__new__(cls, foo). That's right, it passes a foo argument to object.__new__ even though object.__new__ doesn't need it. This is allowed in python 2, but would crash in python 3. It would be best to remove the foo argument from there.

python __new__ - how to implement when not subclassing

Part A
I want to do some checking on arguments to a class instantiation and possibly return None if it doesn't make sense to even create the object.
I've read the docs but I don't understand what to return in this case.
class MyClass:
def __new__(cls, Param):
if Param == 5:
return None
else:
# What should 'X' be?
return X
What should X be in return X?
It cannot be self because the object doesn't exist yet so self is not a valid keyword in this context.
Part B
Tied to my question, I don't understand the need to have the cls parameter.
If you call the constructor of MyClass - var = MyClass(1) - won't cls always be MyClass?
How could it be anything else?
According to the docs, cls in object.__new__(cls[, ...]) is:
. . .the class of which an instance was requested as its first
argument.
(I'm assuming you are using Python 3 because you provided a link to Python 3 docs)
X could be super().__new__(cls).
super() returns the parent class (in this case it is simply object). Most of the times when you are overriding methods you will need to call the parent class's method at some point.
See this example:
class MyClass:
def __new__(cls, param):
if param == 5:
return None
else:
return super().__new__(cls)
def __init__(self, param):
self.param = param
And then:
a = MyClass(1)
print(a)
print(a.param)
>> <__main__.MyClass object at 0x00000000038964A8>
1
b = MyClass(5)
print(b)
print(b.param)
>> None
Traceback (most recent call last):
File "main.py", line 37, in <module>
print(b.param)
AttributeError: 'NoneType' object has no attribute 'param'
You could just return the instance of cls like this return object.__ new__(cls). Because every class is subclass of object, you can use that as a object creator for your class. The returnes object is passed as a first argument to the __init__() with the any number of positional argument or any number of keyword argument you passed to new. There you will create instance variable assigning those values.

Instance variables and functions

I made a post here functions and class attributes (python)
When you define
a class attribute and gave it a function like this:
example 1
def add_age(cls,age):
cls.yrs_old = age
return cls
class Test:
age = add_age
a = Test()
a.age(5)
print(a.yrs_old)
self is automatically passed as the first argument of the add_age function.
However toying around with it more doing the same thing
but this time passing the function as an instance attribute like this:
example 2
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
a = Test(test_func)
print(a.func())
Answers in the linked post stated that all functions in the class are automatically passed a self if the class is instantiated like this:
a = Test(test_func)
Now what's strange here is had I put test_func in a class attribute it works just like my very first example.
If you pass the function in the constructor/init like this:
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
and call it like this:
a = Test(test_func)
print(a.func())
a.func is suddenly acting like a static method as opposed to example 1 where the function defined inside the class attribute becomes a regular class method.
What's going on?.
I thought all functions within a class are implicitly passed a self argument.
After the body of the class statement is evaluated, the metaclass wraps each function in a descriptor which takes care of the distinction between instance, class, and static methods. When you assign the function to an instance attribute, you bypass that machinery, so that the attribute refers to a plain function object.
From documentation -
Any function object that is a class attribute defines a method for instances of that class. It is not necessary that the function definition is textually enclosed in the class definition: assigning a function object to a local variable in the class is also ok.
This means that only methods that are assigned to classes are instance methods for the instances of the class.
Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> b = A()
>>> b.a()
Hmm
>>> b.a
<bound method A.a of <__main__.A object at 0x006D13D0>>
But as soon as you assign a separate function object to the instance variable, it is no longer an instance method , since is is not defined at the class level, it is only defined for that particular instance , Example -
>>> def c():
... print("Hello")
...
>>> b.a = c
>>> b.a()
Hello
>>> b.a
<function c at 0x0017B198>
As you can see, when you directly assigned the function to the instance variable (instead of assigning it to class variable , it is now a normal instance attribute, that references a function object, and not an instance method.
You can also assign functions to class variables after the definition of class , and the instances would automatically get them as instance methods, Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> def c(a):
... print("Hello - ", a)
...
>>> b = A()
>>> A.b = c
>>> b.b
<bound method A.c of <__main__.A object at 0x006D13D0>>
>>> b.b()
Hello <__main__.A object at 0x006D13D0>

Can I instantiate a new object without using function notation? Why?

If I make a simple class like this:
class Foo:
i = 1
j = 2
Can I instantiate a new object by simply using Foo on the right-hand side ( as opposed to saying Foo() )? I would guess not, but I just tried the following and it worked:
finst = Foo
print finst.i
It works, because i is not a property of the object (or instance) but of the class. You are not creating a new instance.
Try:
class Foo:
def bar(self):
print 42
finst = Foo
finst.bar()
Traceback (most recent call last):
File "", line 1, in
TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)
You did not instantiate an object. You just defined a variable in class scope, and accessed it.
Foo by itself is the class object for class Foo:
>>> type(Foo)
<type 'classobj'>
>>> type(Foo())
<type 'instance'>
Your code:
finst = Foo
print finst.i
decodes as:
bind the name finst to the Foo class object.
print the value of the class' attribute i
That's because finst is merely an alias for the class Foo, and i and j are class variables, not instance variables. If you had declared them as instance variables:
class Foo:
def __init__(self):
self.i = 1
self.j = 2
Then your code would cause an error.
To answer your question, no, you must call a constructor to create an instance.

Categories

Resources