python __new__ - how to implement when not subclassing - python

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.

Related

How to use python's getattr feature on a method of a class? [duplicate]

If I have a class ...
class MyClass:
def method(arg):
print(arg)
... which I use to create an object ...
my_object = MyClass()
... on which I call method("foo") like so ...
>>> my_object.method("foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 1 positional argument (2 given)
... why does Python tell me I gave it two arguments, when I only gave one?
In Python, this:
my_object.method("foo")
... is syntactic sugar, which the interpreter translates behind the scenes into:
MyClass.method(my_object, "foo")
... which, as you can see, does indeed have two arguments - it's just that the first one is implicit, from the point of view of the caller.
This is because most methods do some work with the object they're called on, so there needs to be some way for that object to be referred to inside the method. By convention, this first argument is called self inside the method definition:
class MyNewClass:
def method(self, arg):
print(self)
print(arg)
If you call method("foo") on an instance of MyNewClass, it works as expected:
>>> my_new_object = MyNewClass()
>>> my_new_object.method("foo")
<__main__.MyNewClass object at 0x29045d0>
foo
Occasionally (but not often), you really don't care about the object that your method is bound to, and in that circumstance, you can decorate the method with the builtin staticmethod() function to say so:
class MyOtherClass:
#staticmethod
def method(arg):
print(arg)
... in which case you don't need to add a self argument to the method definition, and it still works:
>>> my_other_object = MyOtherClass()
>>> my_other_object.method("foo")
foo
In simple words
In Python you should add self as the first parameter to all defined methods in classes:
class MyClass:
def method(self, arg):
print(arg)
Then you can use your method according to your intuition:
>>> my_object = MyClass()
>>> my_object.method("foo")
foo
For a better understanding, you can also read the answers to this question: What is the purpose of self?
Something else to consider when this type of error is encountered:
I was running into this error message and found this post helpful. Turns out in my case I had overridden an __init__() where there was object inheritance.
The inherited example is rather long, so I'll skip to a more simple example that doesn't use inheritance:
class MyBadInitClass:
def ___init__(self, name):
self.name = name
def name_foo(self, arg):
print(self)
print(arg)
print("My name is", self.name)
class MyNewClass:
def new_foo(self, arg):
print(self)
print(arg)
my_new_object = MyNewClass()
my_new_object.new_foo("NewFoo")
my_bad_init_object = MyBadInitClass(name="Test Name")
my_bad_init_object.name_foo("name foo")
Result is:
<__main__.MyNewClass object at 0x033C48D0>
NewFoo
Traceback (most recent call last):
File "C:/Users/Orange/PycharmProjects/Chapter9/bad_init_example.py", line 41, in <module>
my_bad_init_object = MyBadInitClass(name="Test Name")
TypeError: object() takes no parameters
PyCharm didn't catch this typo. Nor did Notepad++ (other editors/IDE's might).
Granted, this is a "takes no parameters" TypeError, it isn't much different than "got two" when expecting one, in terms of object initialization in Python.
Addressing the topic: An overloading initializer will be used if syntactically correct, but if not it will be ignored and the built-in used instead. The object won't expect/handle this and the error is thrown.
In the case of the sytax error: The fix is simple, just edit the custom init statement:
def __init__(self, name):
self.name = name
Newcomer to Python, I had this issue when I was using the Python's ** feature in a wrong way. Trying to call this definition from somewhere:
def create_properties_frame(self, parent, **kwargs):
using a call without a double star was causing the problem:
self.create_properties_frame(frame, kw_gsp)
TypeError: create_properties_frame() takes 2 positional arguments but 3 were given
The solution is to add ** to the argument:
self.create_properties_frame(frame, **kw_gsp)
As mentioned in other answers - when you use an instance method you need to pass self as the first argument - this is the source of the error.
With addition to that,it is important to understand that only instance methods take self as the first argument in order to refer to the instance.
In case the method is Static you don't pass self, but a cls argument instead (or class_).
Please see an example below.
class City:
country = "USA" # This is a class level attribute which will be shared across all instances (and not created PER instance)
def __init__(self, name, location, population):
self.name = name
self.location = location
self.population = population
# This is an instance method which takes self as the first argument to refer to the instance
def print_population(self, some_nice_sentence_prefix):
print(some_nice_sentence_prefix +" In " +self.name + " lives " +self.population + " people!")
# This is a static (class) method which is marked with the #classmethod attribute
# All class methods must take a class argument as first param. The convention is to name is "cls" but class_ is also ok
#classmethod
def change_country(cls, new_country):
cls.country = new_country
Some tests just to make things more clear:
# Populate objects
city1 = City("New York", "East", "18,804,000")
city2 = City("Los Angeles", "West", "10,118,800")
#1) Use the instance method: No need to pass "self" - it is passed as the city1 instance
city1.print_population("Did You Know?") # Prints: Did You Know? In New York lives 18,804,000 people!
#2.A) Use the static method in the object
city2.change_country("Canada")
#2.B) Will be reflected in all objects
print("city1.country=",city1.country) # Prints Canada
print("city2.country=",city2.country) # Prints Canada
It occurs when you don't specify the no of parameters the __init__() or any other method looking for.
For example:
class Dog:
def __init__(self):
print("IN INIT METHOD")
def __unicode__(self,):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
When you run the above programme, it gives you an error like that:
TypeError: __init__() takes 1 positional argument but 6 were given
How we can get rid of this thing?
Just pass the parameters, what __init__() method looking for
class Dog:
def __init__(self, dogname, dob_d, dob_m, dob_y, dogSpeakText):
self.name_of_dog = dogname
self.date_of_birth = dob_d
self.month_of_birth = dob_m
self.year_of_birth = dob_y
self.sound_it_make = dogSpeakText
def __unicode__(self, ):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
print(id(obj))
If you want to call method without creating object, you can change method to static method.
class MyClass:
#staticmethod
def method(arg):
print(arg)
MyClass.method("i am a static method")
I get this error when I'm sleep-deprived, and create a class using def instead of class:
def MyClass():
def __init__(self, x):
self.x = x
a = MyClass(3)
-> TypeError: MyClass() takes 0 positional arguments but 1 was given
You should actually create a class:
class accum:
def __init__(self):
self.acc = 0
def accumulator(self, var2add, end):
if not end:
self.acc+=var2add
return self.acc
In my case, I forgot to add the ()
I was calling the method like this
obj = className.myMethod
But it should be is like this
obj = className.myMethod()

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.

Calling #classmethod decorated method throws a TypeError

I am trying to figure out difference between #staticmethod and #classmethod. The latter is passed a cls instance.
When I tried to call the #classmethod, it is giving me an error.
How should I call a #classmethod (to_c() and to_f()) decorated method in REPL?
Here is the REPL calls
>>> from temperature_converter import *
>>> c = TemperatureConverter(41)
>>> TemperatureConverter.to_f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Repos\Python\module-3\temperature_converter.py", line 21, in to_f
return cls.c_to_f(cls.temperature)
File "C:\Repos\Python\module-3\temperature_converter.py", line 25, in c_to_f
return celsius * 9 / 5 + 32
TypeError: unsupported operand type(s) for *: 'property' and 'int'
Here is the class, TemperatureConverter
class TemperatureConverter:
_temperature = 0
#property
def temperature(self):
return self._temperature
#temperature.setter
def temperature(self, value):
self._temperature = value
def __init__(self, temperature):
self.temperature = temperature
#classmethod
def to_c(cls):
return cls.f_to_c(cls.temperature)
#classmethod
def to_f(cls):
return cls.c_to_f(cls.temperature)
#staticmethod
def c_to_f(celsius):
return celsius * 9 / 5 + 32
#staticmethod
def f_to_c(fahrenheit):
return (fahrenheit - 32) * 5/9
Problem: You trying to access the temperature property from the class and expecting the same result as an instance, in this case an int value of 41. However, attributes are called differently in instances and classes. Compare the __get__ method for each:
Description
# Reassignments for illustration
C = TemperatureConverter # class
i = c # instance
attr = "temperature" # attribute
# Call via Instance binding, `c.temperature`
C.__dict__[attr].__get__(i, C)
# 41
# Call via Class binding, `C.temperature`
C.__dict__[attr].__get__(None, C)
# <property at 0x4ab9458>
Here is more information on the signature of the __get__ method and a SO post on how descriptors work. In this case, the signature is could be seen as C.__get__(self, inst, cls). In short, when getting from a property, unlike an instance calls, calls from a class passes None for the instance argument.
As shown above, the property object is returned if bound to the class:
C.temperature
# <property at 0x4ab9458>
Can we still "get" the value from the property object? Let us call __get__ on the property object:
C.temperature.__get__(i, C)
# 41
The latter shows it is possible to get the property value while bound to a class. While it may be tempting to implement the __get__ method on cls.temperature inside your class method, you still need to pass in an instance (e.g. i) to access the value of the property. Instances are not accessible in a class method. Therefore, we see why the property object is returned in your code, which is trying to multiply with an int and raises the error you observe.
This is one explanation for your problem, in particular describing why you cannot access your property value cls.temperature within a class method.

must be called with Obj instance (got tuple instead)

I'm having a class with 2 attributes , and I have a function that generates object from a tuple , but looks like Python refuses to pass a tuple as an argument
here is the code :
class Obj(object):
def __init__ (self,x,y):
self.x=x
self.y=y
def divide (t):
a=t[0]*2
b=t[1]+t[2]
return Obj(a,b)
r=(5,2)
o=Obj.divide(r)
print(o)
and here's the error
Traceback .......,line 12 , in <module> o=Obj.divide(r)
TypeError: unbound method divide() must be called with Obj instance as first argument (got tuple instance instead)
In Python you need to explicitly include self argument in method signature:
def divide(self ,t):
pass # do your stuff
or if you want it to be a classmethod:
#classmethod
def divide(cls ,t):
pass # do your stuff
PS. you have other errors in your code, but you should be able to figure them out on your own now...

Creating multiple objects within the same class in 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.

Categories

Resources