Python method using class variables, right approach - python

I have a question about python using OOP.
If I have two classes, and a method which takes variables from those classes, I pass the classes as parameters of the method as shown below. I am sure that this is not the right approach, and this is why I would like to know an alternative and more efficient way to do it. Specifically:
class Player(object):
x_player = 5
y_player = 5
class Food(object):
x_food = 10
y_food = 10
def method(Player, Food):
if Player.x_player > Food.x_food:
print('Food behind)
if Player.x_player < Food.x_food:
print('Food in front')
Is this correct? If I use the method def __init(self)__ then I cannot pass those variables in the function.
Thank you

If I use the method def init(self) then I cannot pass those variables in the function.
This understanding is wrong, as you can add parameters to __init__.
How about this? Or you still insist to use class method? If you choose class method, your solution already ok, just call with method(Player, Food) or even you did not need the parameters added in the function.
class Player(object):
def __init__(self, x, y):
self.x_player = x
self.y_player = y
class Food(object):
def __init__(self, x, y):
self.x_food = x
self.y_food = y
def method(player, food):
if player.x_player > food.x_food:
print('Food behind')
if player.x_player < food.x_food:
print('Food in front')
method(Player(5, 5), Food(10, 10))

if those are class variables, you can just refer to those variables as classname.var_name:
class A:
a = 10
if __name__ == '__main__':
print(A.a)
if the values are defined by instance, then you need to instantiate the class variables and then you can perform comparison on that
class A:
def __init__(self, a):
self.a = a
if __name__ == '__main__':
x = A(5)
y = A(10)
print(x.a > y.a)
>> False
Also this is python and in python you don't specify the data types of function arguments so you can send any object and be carefree of which class' instance it is. You code should handle the exceptions if a wrong class' object is sent.
class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, a):
self.a = a
def fun(obj):
print(obj.a)
if __name__ == '__main__':
x = A(5)
y = B(10)
fun(x)
fun(y)
>> 5
>> 10
class A:
def __init__(self, a):
self.a = a
class B:
def __init__(self, a):
self.a = a
def fun(obj):
if not isinstance(obj, B):
print(obj.a)
if __name__ == '__main__':
x = A(5)
y = B(10)
fun(x)
fun(y)
>> 5

Related

Redefining methods through class inheritance in Python

I'm playing around with class inheritance and I'm wondering if its possible to change aspects of an inherited method in the child class without having to rewrite the whole thing?
For example:
class Parent:
def _init_(self, x):
self.x = x
def foo(self):
a = self.x
if a > 0:
forward = True
elif a < 0:
forward = False
return forward
class Child1(Parent):
def foo(self, y=None, bool=False):
if bool:
a = y
else:
a = self.x
super().foo()
class Child2(Parent):
pass
What I'm looking for is if I called Child1.foo it could reassign the variable a before running through
the method, as defined in the parent class. Where a is dependent on the y and bool arguments passed through the rewritten method in Child1:
print(Child1(2).foo(-2, True))
# => False
print(Child1(2).foo())
# => True
print(Child2(2).foo())
# => True
Is this possible, or would I just have to rewrite a new method for each class?
I think I understand your problem and I have some suggestions for how you can resolve this:
Using "private" methods
For example:
class Parent:
def __init__(self, x):
self.x = x
def _foo(self, a=None):
a = a if a else self.x * 2
if a > 10:
over = True
else:
over = False
return over
def foo(self):
return self._foo()
class Child1(Parent):
def foo(self, y=None, condition=False):
if condition:
a = y*2
else:
a = self.x*2
return self._foo(a)
class Child2(Parent):
pass
In this example, all child classes will inherit the _foo "private" function, where they may or may not receive a value of a.
Using abstract classes
There is another solution to this problem with abstract classes (here is an example of how to do this), where you forces the child class to implement the function foo:
Important
Remembering that in the case of abstract classes, if you do not define the function decorated with #abstractmethod, you will receive an error similar to this TypeError: Can't instantiate abstract class Child2 with abstract methods foo
Example:
Python 2.x
from abc import ABCMeta, abstractmethod
class Parent:
__metaclass__ = ABCMeta
def __init__(self, x):
self.x = x
def _foo(self, a=None):
a = a if a else self.x * 2
if a > 10:
over = True
else:
over = False
return over
#abc.abstractmethod
def foo(self):
pass
class Child1(Parent):
def foo(self, y=None, condition=False):
if condition:
a = y*2
else:
a = self.x*2
return self._foo(a)
class Child2(Parent):
def foo(self):
return self._foo()
Python 3.x
class Parent(metaclass=ABCMeta):
def __init__(self, x):
self.x = x
def _foo(self, a=None):
a = a if a else self.x * 2
if a > 10:
over = True
else:
over = False
return over
#abc.abstractmethod
def foo(self):
pass
class Child1(Parent):
def foo(self, y=None, condition=False):
if condition:
a = y*2
else:
a = self.x*2
return self._foo(a)
class Child2(Parent):
def foo(self):
return self._foo()
In both examples you will have the same result by running this:
print(Child1(2).foo(10, True)) // True
print(Child1(2).foo()) // False
print(Child2(2).foo()) // False

Basic confusion with classes & modules (Specially with "self")

I thought that this code would work
class A:
def __init__(self):
self.x = 1
B = self.create_b()
print(B.y)
def create_b(self):
class B:
def __init__(self):
self.y = self.x
return B
A = A()
but I receive the following error
AttributeError: type object 'B' has no attribute 'y'
What am I doing wrong?
You're confusing classes with class instances (not Python modules). In Python class statements are executable and create a callable object that you must then be called to create instance objects of the class that was defined.
Regular methods of a class automatically receive a first argument that's the instance they belong to, and by convention, this argument is usually called self.
Here's what I mean:
class A:
def __init__(self):
self.x = 1
B = self.create_b() # Create B class.
b = B(self) # Create instance of B class passing this instance of A.
print(b.y)
def create_b(self):
class B:
def __init__(self, a_inst):
self.y = a_inst.x
return B
a = A() # -> 1
There are three problems with this code. The first is that since create-b returns a class object, not an instance of the class, B's __init__ was never run. You could solve this with
class A:
def __init__(self):
self.x = 1
B = self.create_b()
b = B()
print(b.y)
def create_b(self):
class B:
def __init__(self):
self.y = self.x
return B
A = A()
The second is that nested classes do not have access to the wrapping method's local namespace like a nested function (closure) would. When attempting self.y = self.x, instances of class B have no special relationship with the instance of A that created them. You could solve this with
class A:
def __init__(self):
self.x = 1
B = self.create_b(self)
b = B()
print(b.y)
def create_b(self):
class B:
def __init__(self, a):
self.y = a.x
return B
A = A()
The third is that python creates a weakref to classes when they are defined that never goes away. Each time you call create_b, you create a small memory leak. You could solve this with
class A:
def __init__(self):
self.x = 1
b = B(self)
print(b.y)
class B:
def __init__(self, a):
self.y = a.x
A = A()

How to make local variable of a method of Superclass available to a subclass where the superclass is called by another class

As you can see the code, I have a super class bar_for_foo_mixin() and I have a subclass myfoo(bar_for_foo_mixin): I am computing a operation self.Z = X+Y in bar() method of superclass.
Now I want the self.z = 0 updated to the computation done in bar() method and inheirt this value to the subclass myfoo(bar_for_foo_mixin): and use it inside subclass.
class bar_for_foo_mixin():
def __init__(self):
self.z = 0
def bar(self, q):
x = 2
y = 8
self.z = x + y + q
class oldfoo():
def __init__(self):
pass
var = bar_for_foo_mixin()
var.bar(10)
class myfoo(bar_for_foo_mixin):
def __init__(self):
super(myfoo, self).__init__()
def hello(self):
print("hello", self.z)
final = myfoo()
final.hello()
Result of the code:
hello 0
Expected result:
hello 20
The bar_for_foo_mixin instance stored in your oldfoo.var class variable is a completely separate instance from the myfoo object you instantiated in the main program, so their instance variable z would not be shared.
If you would like a variable to be shared across all instances of a class, you should make it a class variable instead, and make methods that are dedicated to updating class variables, such as bar_for_foo_mixin.bar, a class method instead:
class bar_for_foo_mixin():
z = 0
#classmethod
def bar(cls, q):
x = 2
y = 8
cls.z = x + y + q
class oldfoo():
def __init__(self):
pass
var = bar_for_foo_mixin()
var.bar(10)
class myfoo(bar_for_foo_mixin):
def __init__(self):
super(myfoo, self).__init__()
def hello(self):
print("hello", self.z)
final = myfoo()
final.hello()
This outputs:
hello 20
You're not even calling the bar method by the new final variable:
class bar_for_foo_mixin():
def __init__(self):
self.z = 0
def bar(self, q):
x = 2
y = 8
self.z = x + y + q
class myfoo(bar_for_foo_mixin):
def __init__(self):
super(myfoo, self).__init__()
def hello(self):
print("hello", self.z)
final = myfoo()
final.bar(10) # <== call it to take effect
final.hello() # ==> hello 20

python; how to pass one argument through multiple methods in a class

I am learning about class structure in python. Would like to know if it's possible to pass one argument through more than one method.
class Example(object):
def __init__(self, x):
self.x = x
def square(self):
return self.x**2
def cube(self):
return self.x**3
def squarethencube(y):
sq = Example.square(y)
cu = Example.cube(sq)
return cu
two = Example(2)
print(two.squarethencube())
Error is on line 10; AttributeError: 'int' object has no attribute 'x'
The goal is to use the 'squarethencube' method to pass '2' to square(), which is 4. Then pass '4' to cube(). The desired output is '64'. Obviously, you can write a function to do the math in a very simple way; the question here is how to use multiple methods.
I understand the error in that .x is getting assigned as an attribute onto the output of cube(sq). I was getting the same error, but on line 7, before I changed the argument to y (from self.x).
I've found some similar answers here but I need a simpler explanation.
Currently, square and cube are methods bound to the class; however, you are accessing them in squarethencube by class name, but they are methods, and thus rely on a reference to the class from an instance. Therefore, you can either create two new instances of the class or use classmethod:
Option1:
class Example(object):
def __init__(self, x):
self.x = x
def square(self):
return self.x**2
def cube(self):
return self.x**3
def squarethencube(self, y):
sq = Example(y).square()
cu = Example(y).cube()
return cu
Option 2: use a classmethod:
class Example(object):
def __init__(self, x):
self.x = x
#classmethod
def square(cls, x):
return x**2
#classmethod
def cube(cls, x):
return x**3
def squarethencube(self, y):
sq = Example.square(y)
cu = Example.cube(sq)
return cu
class Example:
def __init__(self, x):
self.x = x
def square(self):
return self.x**2
def cube(self):
return self.x**3
def squarethencube(self):
return (self.x**2)**3
two = Example(2)
print(two.squarethencube())

Python Class Objects Attribute Referencing

I have two classes. a and b.
In one of class a's methods, I created an object of class b. One of class b attributes takes a function. So say I gave it a random function but does this function of class b have access to class a's attribute? even though I didn't pass it in directly as a parameter?
class b:
def __init__(self):
self.attribute_function = None
class a:
def __init__(self):
self.temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda self: self.counter < self.temp
return obj.attribute_function()
if __name__ == "__main__":
#pass
obj = a()
print obj.temp()
In the above example, I tried to provide a really basic example, but if you run it, it doesn't work...
Revised Code, class a should look like this:
class a:
def __init__(self):
self.temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda args: self.counter < self.temp
return obj.attribute_function(1) # i added this 1 to fill in arg
This works:
class b:
def __init__(self):
self.attribute_function = None
class a:
def __init__(self):
self._temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda self=self: self.counter < self._temp
return obj.attribute_function()
if __name__ == "__main__":
obj = a()
print obj.temp()
On problem you had is self.temp = 10 which shadowed your method temp().
Another problem: lambda self: self.counter < self._temp. Your lambda function was expecting an argument. But omitting self is not a good idea lambda : self.counter < self._temp, because if you call obj.attribute_function() somewhere where self is not available or has changed - it will not find self or use another self. self=self fixes that.
But generally such magic is an anti-pattern. Tell us what are your trying to achieve, and there should be a better way to do what you want. Otherwise this kind of code will ensure many headaches.
I think this is a better solution (called strategy pattern):
class B:
def __init__(self, a):
self.a = a
def temp(self):
return self.a.temp()
class A:
def __init__(self):
self._temp = 10
self.counter = 0
def temp(self):
return self.counter < self._temp
if __name__ == "__main__":
obj = B(A())
print obj.temp()
Your example does not work because you have a name collision at temp
You have assigned temp to be both a method:
def temp(self):
and an attribute:
self.temp = 10

Categories