I have a class with several methods. Outputs of a method are used in other methods. I don't want to pass these variables as input argument to other methods (to make code more simple).
I can add output of this method into self so I have access to these variables in other methods.
But, I want to be sure that it is a standard implementation. I am worried that it may cause unpredictable error. If you have experience in this regard, please let me know if the following example is a correct implementation or not.
class MyClass:
def method_1(self, A):
return A + 1
def method_2(self):
return self.B + 10
def method_3(self, C):
self.B = self.method_1(C)
result = self.method_2()
return result
z = MyClass()
z.method_3(1)
In the above example, I don't need to pass self.B into method_2. This code works but I want to be sure that it is a standard way.
The real program I working on is complicated, so I made a simple example for this question.
Yup it is more or less correct but the standard way of doing something like this is having a __init__() method and using function annotations.
class MyClass:
def __init__(self) -> None:
self.B = 0
def method_1(self, A: int) -> int:
return A + 1
def method_2(self) -> int:
return self.B + 10
def method_3(self, C: int) -> int:
self.B = self.method_1(C)
result = self.method_2()
return result
z = MyClass()
z.method_3(1)
Where method_2() relies on an attribute that may be unset, make it private so that people aren't tempted to use it. For example, what if I did this?
>>> z = MyClass()
>>> z.method_2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 9, in method_2
return self.B + 10
AttributeError: 'MyClass' object has no attribute 'B'
For that matter, it's probably best to make the attribute private too. So:
class MyClass:
def method_1(self, A):
return A + 1
def _method_2(self):
return self._B + 10
def method_3(self, C):
self._B = self.method_1(C)
result = self._method_2()
return result
By the way, where method_1() doesn't use self, consider making it a staticmethod.
Related
In Python, when I'm defining a function inside a class, I can include self as one of the arguments to access the member variables of that class, and I can also choose not include self as the argument if I don't need to access its member variables. But then I discovered that if the function does not have self as arguments, then it becomes invisible to other functions in that class. For example
class Test:
def __init__(self, val:int) -> None:
self.a = val
def f(a:int) -> int:
return a + 1
def g(self) -> int:
return f(self.a)
if __name__ == '__main__':
t = Test(2)
print(t.g())
The above codes will lead to the following error message:
Traceback (most recent call last):
File "/Users/louchenfei/Downloads/lc.py", line 11, in <module>
print(t.g())
File "/Users/louchenfei/Downloads/lc.py", line 7, in g
return f(self.a)
NameError: name 'f' is not defined
I wonder why that's the case and what are the rules for visibilities of functions defined in a class?
Its difficult to know what you want, but one solution is to make f a staticmethod. Still you need to have a reference to Test in some way:
class Test:
def __init__(self, val:int) -> None:
self.a = val
#staticmethod
def f(a:int) -> int:
return a + 1
def g(self) -> int:
return self.f(self.a)
if __name__ == '__main__':
t = Test(2)
print(t.g())
Here, the call is self.f(self.a).
The method f is not in scope inside another method.
Very simple question here, but I am new to python as an object oriented programming language. I am trying to write a class. Imagine it is organized as follows:
class myClass:
def __init__(self,a,b,runit=True):
self.a = a
self.b = b
if runit:
self.run_func()
def run_func(self):
self.c = self.a*self.b
return
So as you can see the class is initialized with just a and b. It defaults to initialize c from those arguments, but it need not. Now let me illustrate three use cases that I think should behave the same, but are not:
# Use 1
test = myClass(5,2)
print(test.c)
# Use 2
test = myClass(5,2,runit=False)
test.run_func()
print(test.c)
# Use 3
test = myClass(5,2,runit=False).run_func()
print(test.c)
This returns the following:
10
10
Traceback (most recent call last):
File "<ipython-input-37-cb854baa3a0c>", line 23, in <module>
print(test.c)
AttributeError: 'NoneType' object has no attribute 'c'
Why can the instantiated class not be operated on immediately and pipe this result to test in one step? In my head, (1) and (2) are the same set of operations except one is broken into two steps and the other is done in one line.
And, more importantly, how can I fix this by editing my class to behave in the expected manner?
at # Use 3 myClass(5,2,runit=False).run_func() returns None
to fix you could return self:
def run_func(self):
self.c = self.a*self.b
return self
test = myClass(5,2,runit=False).run_func()
print(test.c)
output:
10
or you should not set your flag runit to False and use:
class myClass:
def __init__(self,a,b,runit=True):
self.a = a
self.b = b
if runit:
self.run_func()
def run_func(self):
self.c = self.a*self.b
test = myClass(5,2)
print(test.c)
output:
10
run_func should return self:
def run_func(self):
self.c = self.a*self.b
return self
stating return without a value following it, is the same as writing return None.
You get this exception because None has no attribute named c.
In the 3rd case, test is assigned the return value of run_func(). It has a bare return which returns None, so test = None.
In case 3
test = myClass(5,2,runit=False).run_func()
print(test.c)
test is not the object, but the return of the run_func(), which is null and indeed has no c attribute
Lets look at the method call in question:
test = myClass(5,2,runit=False).run_func()
Let's break that down. First, you construct an instance of myClass and then you call run_func on that instance. It's the return value of run_func that you assign to test and since run_func doesn't return anything test is None resulting in your error.
As another way to see this, try the following:
class myClass:
def __init__(self,a,b,runit=True):
self.a = a
self.b = b
if runit:
self.run_func()
def run_func(self):
self.c = self.a*self.b
return 11
test = myClass(5,2,runit=False).run_func()
print(test) # will print 11
Take a look at the code below.
I want to use the value of a as the default value for argument c in the declaration of the classmethod my_method().
How can I do it? The example below fails.
>>> class X:
... a = 'hello'
... def __init__(self, b):
... self.b = b
... #classmethod
... def my_method(cls, c=X.a):
... print 'cls.a = {}'.format(cls.a)
... print 'c = {}'.format(c)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in X
NameError: name 'X' is not defined
It really depends what you want, but one way to get what you seem to want is to do it like this.
class X:
a = 'hello'
def __init__(self, b):
self.b = b
#classmethod
def my_method(cls, c=None):
if c is None:
c = X.a
print('cls.a = {}'.format(cls.a))
print('c = {}'.format(c))
X.my_method()
Make the default None and check for None in the function body, assigning to c if it is found to be None. If you ever wanted None to be a valid argument for the function then it wouldn't work, but that doesn't seem likely.
This idiom is often used in python to avoid the pitfalls mutable default argument, but here it delays the assignment from X.a until after X has actually been defined.
It also means that if you change X.a then my_method will pick up the new value. You may or may not want that.
Apologies for incorrect lingo, I am still new to this.
I want to make a class initialiser that, using a conditional, will decide whether or not the instance of said class will collapse into a simple integer.
Simplified Unworking Example:
class A(object):
def __init__(self,a,b):
self.a = a
self.b = b
if self.b == 0:
return int(a)
def __repr__(self):
return str(a)+":"+str(b)
DoesntBecomeAnInt = A(3,4)
WillBecomeAnInt = A(3,0)
print(DoesntBecomeAnInt,WillBecomeAnInt)
##Desired Output:
##3:4, 3
Any help would be very much appreciated!
You should use the magic method __new__ for this. __new__ is used as a factory where you can decide which class should be instantiated.
class A(object):
def __new__(self, a):
return int(a)
A(4)
> 4
A(4).__class__
> <type 'int'>
class A:
def __new__(cls, a, b):
if b == 0:
return a
return super().__new__(cls)
def __init__(self, a, b):
print('Initilizing')
self.a = a
self.b = b
def __repr__(self):
return str(self.a)+":"+str(self.b)
__new__ is the method used to control the creation of new objects (hence the name). Here we check if b is zero in __new__ and return an instance of the appropriate type.
In action:
>>> DoesntBecomeAnInt = A(3,4)
Initilizing
>>> WillBecomeAnInt = A(3,0)
>>> print(DoesntBecomeAnInt,WillBecomeAnInt)
3:4 3
You don't.
The behavior you desire is completely unexpected and somewhat bizarre. Calling A() is expected to return an instance of A. Doing anything else is confusing and unintuitive, which makes it difficult to read and understand any code invoking it.
Alternative
If you really need this behavior, create a factory method:
def make_thing(a, b):
if 0 == b:
return int(a)
else:
return A(a, b)
Obviously, you need a better name than make_thing, but without any context, I can't give you any suggestions.
Avoid the problem if possible
Since A is not a number and is generally not compatible with int, it is also somewhat strange to store both int and A in the same variable.
If all you're doing is converting to a string, then you don't need a class at all. A simple method outside of a class is the better alternative:
def a_and_b_to_string(a, b):
if b == 0:
return str(int(a))
else:
return str(a) + ":" + str(b)
If you're doing more than that, your calling code will probably end up looking something like this:
x = make_thing(input1, input2)
if isinstance(x, A):
result = x.some_method_from_a() # Or some other calculation requiring an A
else:
result = 5 * x # Or some other calculation requiring an `int`
This is somewhat silly: you write a method to choose the data type and then have to write specialized code for each possible result. You're not getting any benefits from having a function that returns the separate types here. I can think of two simpler alternatives:
Just move the check to the calling code:
if input2 == 0:
temp = A(input1, input2)
result = temp.some_method_from_a() # Or some other calculation requiring an A
else:
result = 5 * int(input1) # Or some other calculation requiring an int
If you go this route, you should also modify A.__init__ to throw a ValueError if b == 0, since that would be an invalid state for an A to be in.
Modify A so that it works properly regardless of whether b is 0:
class A(object):
def __init__(self,a,b):
self.a = a
self.b = b
def some_method_from_a():
if self.b == 0:
# Some calculation involving only a
return int(self.a) * 5
else:
# Some other more complex calculation involving both a and b
return self.a * self.b * 6
def __repr__(self):
if self.b == 0:
return str(int(self.a))
else:
return str(self.a) + ":" + str(self.b)
Then
x = A(a, b)
result = x.some_method_from_a()
But again, it's hard to provide recommendations without knowing how you're actually using it.
Using dataclasses
# A(...) should either raise an exception or
# return an instance of A to avoid confusion.
# create can do whatever you want it to.
import dataclasses
#dataclasses.dataclass
class A:
print('Initializing')
a : int
b : int
def __repr__(self):
if self.b == 0:
return str(int(self.a))
else:
return str(self.a) + ":" + str(self.b)
#classmethod
def create(cls, a, b):
if b == 0:
return a
return cls(a, b)
DoesntBecomeAnInt = A.create(3,4)
WillBecomeAnInt = A.create(3,0)
print(f'{DoesntBecomeAnInt}\n{WillBecomeAnInt}')
Initializing
3:4
3
[Program finished]
I recently saw something like this in a python library (PyTorch):
class A(BaseClass):
def __init__(self,b,c):
self.b =b
self.c = c
def forward(self,d,e):
return self.b + self.c + d + e
a_instance = A(1,2)
assert a_instance(d=4,e=5) == a_instance.forward(4,5)
where instead of directly calling the method "forward" inside the Class A, you can just pass the arguments to an instance of the class and under the hood it passes them to the "forward" function and calls it.
I very much like to know how to implement something like this.
Any explanation to shed a light on this is very much appreciated.
PS: This is my made up example not the real thing.
You can use __call__ method.
class A():
def __init__(self,b,c):
self.b =b
self.c = c
def forward(self,d,e):
return self.b + self.c + d + e
def __call__(self,d,e):
return self.forward(d,e)
a_instance = A(1,2)
assert a_instance(d=4,e=5) == a_instance.forward(4,5)
This depends on how BaseClass is defined. For example in a regular situation you would have something as follows:
>>> class B(object):
def __init__(self):
pass
>>> B("sa")
Traceback (most recent call last):
File "<pyshell#85>", line 1, in <module>
B("sa")
TypeError: __init__() takes exactly 1 argument (2 given)
If someone defined BaseClass as a metaclass though you would have a situation similar to this:
```
>>> class CallableClass(type):
def __new__(self, a):
print "Look at me, making life harder for everyone: "+str(a)
>>> class B(CallableClass):
def __init__(self):
pass
>>> B("Do I really need to?")
Look at me, making life harder for everyone: Do I really need to?
```
To get what you want you would do something like
>>> class CallableClass(type):
def __new__(self, a):
print "Look at me, making life harder for everyone: "+str(a)
self.a = a
return self
>>> class B(CallableClass):
def __init__(self):
pass
>>> a = B("Do I really need to?")
Look at me, making life harder for everyone: Do I really need to?
>>> a.a
'Do I really need to?'
but as my example exemplifies: why? 9/10 times you don't really need metaclasses and you really really, really, don't want them mixed up with your work - it's hard to debug, it's hard to write, it's hard to think about. Maybe this is an xyz question?
EDIT:
>>> class IsCallable(object):
def __init__(self):
print("this is init")
def __call__(self):
print("this is call")
>>> class C(IsCallable):
def __init__(self):
IsCallable.__init__(self)
pass
>>> C()
this is init
<__main__.C object at 0x7f32b98ebdd0>
>>> C()()
this is init
this is call