Set the value of the argument in the class that inherits from int or float or str

The error occurs when I try to set the value of a class argument that inherits from str . The error only occurs when I try to access an argument using myClass(arg = 'test') . Error:

 TypeError: 'arg' is an invalid keyword argument for this function 

The problem is shown in this example:

 class A(str): def __init__(self, arg): pass A("test") # Works well A(arg = "test") # Fails 

Only the last line causes an error. The previous line works well. The problem is similar with classes that inherit from int or float .

UPDATE (decision):

I found a solution with these links:

Decision:

 class A(str): def __new__(cls, *args, **kwargs): return str.__new__(cls) def __init__(self, arg01): print(arg01) A(arg01= "test") 

I don’t know exactly why this works, and I will investigate it. If anyone has a clear explanation, I am interested, and I thank him in advance.

UPDATE (my explanation):

I'm not sure what I will say, but this is what I understood.

Imagine the class 'myClass' without any property. When I do this myInstance = myClass() , this is what happens:

The myClass.__new__ method is myClass.__new__ . This method will create the myInstance object. __new__ is a real constructor ( __init__ not a constructor!). In the __new__ pseudo code, __new__ look something like this:

 def __new__ (cls, *args, **kwargs): myInstance = # Do some stuff to create myInstance, an object of the type passed as argument (cls). # Add the method __init__ to myInstance. # Call __init__ and pass to it the list 'args' and the dictionary 'kwargs' (both come from arguments of __new__). Pass to it also the object itself (self) : obj.__init__(self, args, kwargs) : # Do some stuff. 

The situation is slightly different when we use immutable types (str, int, float, tuple). In the previous pseudocode, I wrote def __new__(cls, *args, **kwargs) . With immutable types, the pseudo code for the __new__ method __new__ more like this def __new__(cls, anUniqueValue) . I do not understand why the behavior of immutableTypes.__new__ is a rejection of others, but this is the case. You can see this in this example:

 class foo(): def __init__(self): pass foo.__new__(foo, arg = 1) # That works because the method __new__ look like this : def __new__(*args, **kargs). str.__new__(str, arg = 1) # That fails because we are trying to pass the attribute 'arg' to a method which look like this : def __new__(anUniqueValue). 

From there, we can understand why the solution presented earlier works. We are editing the __new__ method of __new__ immutable type, which works the same as a mutable type.

 def __new__(cls, *args, **kwargs): return str.__new__(cls) 

These 2 lines convert def __new__ (cls, anUniqueValue) to def __new__ (cls, *args, **kwargs)

I hope my explanation is almost clear and error free. If you speak French, you can learn more at this link: http://sametmax.com/la-difference-entre- new -et- <strong> INIT - python /

+5
source share
1 answer

What you wrote is essentially correct, there are several errors here and there.


 class A(str): def __new__(cls, *args, **kwargs): return str.__new__(cls) def __init__(self, arg01): print(arg01) 

This is not entirely correct: if you do not pass any argument to str.__new__ , your new instance will be equivalent to an empty string.

 class A(str): def __new__(cls, arg): return str.__new__(cls, arg) def __init__(self, arg): print(arg) 

In general, __new__ and __init__ should have the same signature. This is not a requirement, but in this case you need to pass arg to str.__new__ so that you intercept arg .


The myClass.__new__ method is myClass.__new__ . This method will create the myInstance object. __new__ is a real constructor ( __init__ not a constructor!). In the __new__ pseudo code, __new__ look something like this:

The answer of __new__ not to call __init__ , as shown in this simple example:

 class C: def __new__(cls): print('entering __new__') self = super().__new__(cls) print('exiting __new__') return self def __init__(self): print('entering __init__') super().__init__() print('exiting __init__') C() # Output: # entering __new__ # exiting __new__ # entering __init__ # exiting __init__ 

As you can see, in my __new__ I did not call __init__ explicitly, and object.__new__ did not call __init__ .

__init__ automatically called by the Python interpreter itself whenever __new__ returns an instance of the type.


The situation is slightly different when we use immutable types (str, int, float, tuple).

This is not entirely true. The situation is different when we inherit from a type that does not use the default __new__ implementation .

The default implementation of __new__ (i.e., object.__new__ ) is very permissive and ignores every position argument and keyword argument. If you replace it with a less permissive implementation, then there will be problems similar to the ones you observe.

It is important to understand that the problem does not arise not by default __new__ , but by the fact that our __init__ not compatible with __new__ .


  foo.__new__(foo, arg = 1) # That works because the method __new__ look like this : def __new__(*args, **kargs). str.__new__(str, arg = 1) # That fails because we are trying to pass the attribute 'arg' to a method which look like this : def __new__(anUniqueValue). 

Did you understand. One bit is wrong: the correct signature for str.__new__ :

 def __new__(cls[, object[, encoding[, errors]]]) 

All arguments except cls are positional and keyword arguments. In fact you can do:

 >>> class C(str): ... def __init__(self, object): ... pass ... >>> C('abc') 'abc' >>> C(object='abc') 'abc' 

You see? We used a signature for __init__ , which is compatible with str.__new__ , and now we can avoid overriding __new__ !

+2
source

Source: https://habr.com/ru/post/1239227/


All Articles