Calling other class methods from __new__() in python - python

I have created a class foo as below:
class foo():
def __new__(cls, a, b, c, add=True):
return cls.sum(a, b, c) if add else cls.subtract(a, b, c)
def sum(a, b, c):
return a + b + c
def subtract(a, b, c):
return c - b - a
print(foo(1, 2, 3, True))
This program returns the required result as 6. However, I needed to get the clarity of a few concepts:
Is using this methodology or design of OOP correct or is there a better way to do it? I want the class to return a value or any other object(not it's own class instance)
Regardless of the structure above, if sum and subtract are instance methods, how can they be called without instantiating an object as in the above example i.e., print(...)?
I have observed many python APIs and frameworks returning an object or a value through class instantiation.
I am trying to understand the core concepts of OOPs in python please help.

The way you have it now, sum and subtract are indeed instance methods.
>>> foo_obj = object.__new__(foo) # This will actually create a foo object to demonstrate
>>> foo_obj.sum
<bound method foo.sum of <__main__.foo object at 0x0000000000000000>>
>>> type(foo_obj.sum)
<class 'method'>
But that's just because when you access them through an instance, Python dynamically creates a method (Basically just binds the first argument to the object, usually self)
But, you can access the wrapped function through the class:
>>> foo_obj.sum.__func__
<function foo.sum at 0x0000000000000001>
>>> foo.sum
<function foo.sum at 0x0000000000000001>
>>> foo_obj.sum.__func__ is foo.sum
True
So in your __new__ function, it won't bind the first argument, and they call the underlying function instead of making them an instance method.
To fix the warnings, you can make them classmethods or staticmethods. But it is generally bad practice to not return an object that is an instance of the class from the __new__. If you really wanted to use OOP, either subclass int or make a wrapper, so you can have:
>>> class Foo:
__slots__ = 'value',
def __init__(self, a, b, c, add=True):
self.value = self.sum(a, b, c) if add else self.subtract(a, b, c)
#staticmethod
def sum(a, b, c):
return a + b + c
#staticmethod
def subtract(a, b, c):
return c - b - a
>>> foo = Foo(1, 2, 3, True)
>>> foo
<__main__.foo object at 0x0000000000000002>
>>> foo.value
6
or
>>> class Foo(int):
__slots__ = ()
def __new__(cls, a, b, c, add=True):
value = cls.sum(a, b, c) if add else cls.subtract(a, b, c)
return super().__new__(cls, value)
#staticmethod
def sum(a, b, c):
return a + b + c
#staticmethod
def subtract(a, b, c):
return c - b - a
>>> foo = Foo(1, 2, 3, True)
>>> foo
6
>>> type(foo)
<class '__main__.Foo'>

Related

Same name for instance method and static method in python

I am writing some small library and I want to provide users two approaches for the same functionality, by instance method and static method. Here is a simplified example:
class ClassTimesAdd(object):
def __init__(self, a, b):
self.a = a
self.b = b
def TimesAdd(self, c):
return self.a * self.b + c
#staticmethod
def TimesAdd(a, b, c):
return a * b + c
print(ClassTimesAdd.TimesAdd(1, 3, 7))
ins = ClassTimesAdd(2, 5)
print(ins.TimesAdd(7))
And you can find that the earlier function will be overwritten and only the last one is valid. I'm wondering if there is some simple method that I can use to make the two approaches both work.

List and use the variables defined inside my own function

Suppose that I have a function like this one:
def foo():
a = 1
b = 2
c = 3
return c
Now can I use the a, bvariables? Even if foo() is not returning them like this?
foo().a + foo().b
I know this is easy to do if foo was a class, but it's not.
You can try function attributes. We know that everything in python is an object. So you can define attributes for a function also since a function is also an object. If you want to use a local function variable outside the function, then it is better to make those variables as the function attributes.
I tried something like below :
def foo():
foo.a = 1 # function attribute
foo.b = 2 # function attribute
c = 3
return c
foo()
print(foo.a + foo.b) # -> 3
Even though you are saying you have a function, not a class, python is able to transcend those differences using callable classes. Those are normal classes whose instances can be called as normal functions while still maintaining an internal state. Here is a demonstration:
class Foo:
def __init__(self):
self.a = self.b = self.c = None
def __call__(self):
self.a = 1
self.b = 2
self.c = 3
return self.c
foo = Foo()
print(foo()) # will print 3
print(foo.a) # will print 1
print(foo.b) # will print 2
In the above code snippet, you can think of Foo as a class that generates objects (int this case foo), each being a function. Your code can call foo() and what this will do, is execute the __call__() method of that class. Since however __call__() uses self.a, self.b, self.c instead of local variables a, b, c, their values after the execution of the function will be saved inside the foo object. So you can check them just by doing foo.a and so on.
If you would like to add more arguments in your original foo() function, simply add them to the __call__() method and assign values to them when calling foo().
You cannot use them like that.You can return all 3 variables and use them accordingly.
Try:
def foo():
a = 1
b = 2
c = 3
return a,b,c
a,b,c = foo()
Alternatively you can create function variables as below.
def foo():
foo.a = 1
foo.b = 2
foo.c = 3
foo()
print(foo.a, foo.b, foo.c)

Changing Method called without new instance of Python Class

I'm new to Python OOP and for the purpose of this question I have simplified my problem to this:
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
# some arbitrary change
return self.a + self.b
def subtract(self):
# some arbitrary change
return self.a - self.b
a = Foo(a=1, b=2).add()
b = Foo(a=1, b=3).subtract()
So I have an object, which has 2 methods which do different things, in order for me to get some output, I have created 2 separate instances of Foo as the value b has changed.
Is there a way for me to just dynamically set b and the obj.method() without just listing them one after the other? I.E: some sort of generic class that I can use to dynamically set the attributes and the methods that are present in the object? or is there anything built in I can use...
Edit
Here is another example:
class Foo:
def __init__(self, a, b):
self.a = list(a)
self.b = list(b)
def method1(self):.
# some arbitrary change in data
return self.a * 2
def method2(self):
return self.b + [5, 6, 4]
a = Foo(a=[1, 2, 3], b=[]).method1()
b = Foo(b=[1, 2, 3], a=[]).method2()
print(a)
print(b)
So here, the input list changes based on the method called, is there a way for me to package this up so I could feed just one instance some data and then it 'knows' that list a is for method1(), list b is for method2() - I want to use the word reflection but I feel like that might not be accurate.
Again I'm new to OOP so any advice is appreciated
class Foo:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
fo = Foo()
a = fo.add(1,2)
b = fo.subtract(1,3)
you don't need 2 instances of Foo to achieve this.
Just do something like this:
foo = Foo(a = 1, b = 2)
# Perform addition (now 'a' is 1 and 'b' is 2)
a = foo.add()
# Change 'b'
foo.b = 3
# Now perform subtraction (now 'a' is 1 and 'b' is 3)
b = foo.subtract()

Returning the same type as called with when inheriting overrided operators

Imagine this situation in Python:
class A:
def __init__(self, v):
self.val = v
def __add__(self, b):
return A(self.val + b.val)
class B(A):
def __init__(self, v):
super().__init__(v)
It's clear that B inherits the __add__ operator override from its parent, A.
If I have some instances of these objects, and perform some addition:
foo = A(3)
bar = B(4)
baz = B(5)
alp = foo + foo
bet = foo + bar
gam = bar + baz
alp has the type A, which is expected. bet also does, which is maybe less expected, but not surprising.
gam has type A, however, which is irritating, because we've added two instances of B to get it.
My question is: how can this be done so that gam has type B? Does it require overriding each operator (e.g. __add__) on B explicitly to cast the result of super().__add__(b) to the type of B? Or is there a smarter, cleaner way of getting this to work?
Thank you.
instead of calling A(...) call type(self)(...). The type(self) will return the class of the current object.

Binding a method to another class

class A(object):
def a(self, b=1):
print 'Up'
d = {1 : a}
def b( self ):
print self.d[1]
print self.b
print self.d[1].__get__( self, A )()
# print self.d[1]()
class B( object ):
def a( self ):
print 'here??'
return 10000
d = {1 : a}
def b( self ):
print 'hurray'
o = A()
o.b()
b = B()
type( o ).__dict__['b'].__get__( b, type( b ) )()
Hi Folks,
I was going through Python: Bind an Unbound Method? and http://users.rcn.com/python/download/Descriptor.htm and trying to experiment on my learning.
But, I have hit some new doubts now:-
In the last line of my code, I'm able to use __get__ with b object and instance: type(b). This only works if method b is defined in class B. Why is it so?
Even though the last line requires me to provide a method b in class B, still the method b in class A gets called. Why is it so?
To my utter surprise, after the above step, I notice that the method a of class A is not called by the code of method b of class A; instead, it calls the method a of class B. Why is it so?
I'm quite confused after seeing this behaviour. I might also need to learn more on descriptors. But, it would be a great help if you could answer my doubts
In the last line of my code, I'm able to use __get__ with b object and instance: type(b). This only works if method b is defined in class B. Why is it so?
You have to define a method b in class B, because in A.b you have print self.b. Here, self is an instance of the B class, so self.b means "the b method belonging to this B", not "the b method belonging to the class that this method exists in". If you delete print self.b, then the code will work even if B has no b.
Even though the last line requires me to provide a method b in class B, still the method b in class A gets called. Why is it so?
A.b is being called because you are explicitly accessing it with type( o ).__dict__['b']. Whether you bind that method to an A instance or a B instance doesn't matter; it's still A.b.
To my utter surprise, after the above step, I notice that the method a of class A is not called by the code of method b of class A; instead, it calls the method a of class B. Why is it so?
Even though b belongs to the class A, the self you pass to it is still an instance of the B class. Any attributes you access on that self will be B attributes, and any methods you call on it will be B methods.

Categories

Resources