Python: Am I using "self" correctly? - python

I'm new to Python and am only now starting to see the uses of self and would like to know if I am using it correctly. I have some sample code below and was wondering if someone could just skim through and see if it's the correct usage. I'm not sure if it's correct because I seem to be needing to use self a lot but perhaps that is just the style of the language. Thank You
Code
from tkinter import Canvas
class BouncyBall:
def __init__(self):
self.x = 0
self.y = 0
self.d = 15
self.color = 'blue'
self.speed = 2
self.move_left = False
self.move_right = False
def __init__(self, x, y, d, color):
self.x = x
self.y = y
self.d = d
self.color = color
self.speed = 2
self.move_left = False
self.move_right = False
#Accessor Methods
def get_x(self):
return self.x
def get_y(self):
return self.y
def get_diameter(self):
return self.d
def get_color(self):
return self.color
def get_speed(self):
return self.speed
def moving_right(self):
return self.move_right
def moving_left(self):
return self.move_left
#Mutator Methods
def set_x(self, x):
self.x = x
def set_y(self, y):
self.y = y
def set_diameter(self, d):
self.d = d
def set_color(self, color):
self.color = color
def set_speed(self, speed):
self.speed = speed
def set_move_right(self, move_right):
self.move_right = move_right
def set_move_left(self, move_left):
self.move_left = move_left
def draw_ball(self, canvas):
if isinstance(canvas, Canvas):
canvas.create_oval(self.x, self.y, self.x + self.d, self.y + self.d, fill=self.color)
else:
print("Improper Parameter Sent In")

You are using self correctly. It does tend to appear a lot in Python compared with other languages like C++ where the this parameter is implicit.
However, in Python it is not conventional to write get() and set() methods for everything, as you have done. You can reduce your code quite a bit by removing those--and remove a lot of selfs in the process.

Well, first off your __init__ is wrong. Python doesn't allow two definitions of a function/method with the same name in the same namespace, whether or not the prototype differs. I'd suggest dropping your first definition, and changing the def line of the second to:
def __init__(self, x=0, y=0, d=15, color='blue'):
which will do what you wanted (allow you to initialize without arguments by using default values).
You also probably want to drop all your set_ and get_ methods. If the attributes are read/write, just access them normally without getters and setters. If at some later point you need to make them read-only, or compute them, you can rename the attribute to have a leading underscore (e.g. _x) and use #property decorators to continue providing attribute-like access (with or without writability). That would instantly remove the vast majority of (unnecessary) accessor and mutator methods that make you reference self so often. For example, if x should be read-only, you'd set self._x = x in your __init__, then define a property:
#property
def x(self):
return self._x
and users would continue to read it as if it were a simple attribute, they just couldn't write it by accident (they could directly write _x, but that's their problem; Python's philosophy is that we're all adults, and if you ignore the convention that underscore prefixes are internal implementation details, the consequences are on your head).
Otherwise, yes, you'll be referencing self a lot. Python prefers explicit namespacing to implicit, so you use self to differentiate between instance access and scoped variable access. If you are going to be using a given variable a lot (and it won't change during the course of a method), you can cache it to a local name and use the local name unqualified, e.g.:
def some_method(self):
# We use x a lot and never change it, so cache up front:
x = self.x
# Can read cached x over and over without qualification for rest of method

Related

pass arguments inside splitted methods

I need to split class methods in several files. Functionality need to by that I can pass inside method all variables defined in self and receive new self variables defined inside the method.
My attempt:
Below code works, but I don't know if this is the best/proper solution.
Base:
from calculate_function import function
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.z, self.result = function(self)
calculate_function.py:
def function(self):
z = 2
result = z + self.x
return z, result
For above I pass self inside new function for collect all init variables, then define new self variable/results.
There will by much more functions inside different files that will done some calculations and create new variables for instance of class.
Question
What I need is to pass each created self variable to each function.
For above code the solution is proper defined or there is better option to this?
If you want to externalize some part of your class code to external functions, it's better to write those as pure functions and keep the attribute access (and even more attributes updates) within the class code itself - this makes the code much easier to test, read and maintain. In you case this would looks like:
from calculate_function import function
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.z, self.result = function(self.x)
calculate_function.py:
def function(x):
z = 2
result = z + x
return z, result
The points here are that 1/ you can immediatly spot the creation of attributes z and result and 2/ you can test function() without a Data instance.
I need to split class methods in several files.
This often means your class has too many responsabilities. Some parts of it can be delegated to pure functions like shown above. Some other parts, that need access to a common subset of your class attributes, can be delegated to other, smaller, specialized classes - but preferably using composition / delegation instead of inheritance (depending on concrete use cases of course).
You dont need pass self inside the function
Why not do it like this:
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.function()
def function(self):
self.z = 2
self.result = self.z + self.x
Do wish to use another Class function or just a stand alone function?
Here is solution, using class inheritance:
-- function1.py --
class FunctionClass1():
def function1(self):
self.result = self.x + self.y
-- function2.py --
class FunctionClass2():
def function2(self):
self.result = self.result + self.z
-- data.py --
from function1 import FunctionClass1
from function2 import FunctionClass2
class Data(FunctionClass1, FunctionClass2):
def __init__(self):
self.x = 1
self.y = 2
self.z = 3
self.function1()
self.function2()

The pythonic way to construct a multimethod setter

We can use a #property to construct a getter and setter. This is a short example how we can do this:
class A:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 100:
self.__x = 100
else:
self.__x = x
My case seems to be more complicated.
class A:
def __init__(self, x):
self.__x = x
self.x1()
self.x2()
self.x3()
def x1(self):
self.__x1 = self.__x + 1
return self.__x1
def x2(self):
self.__x2 = self.__x1 + 2
return self.__x2
def x3(self):
self.__x3 = self.__x2 + 3
return self.__x3
if __name__ == "__main__":
a = A(3)
print(a.x3)
Methods x1, x2 and x3 are oversimplified. The self.__x3 variable is set only once, when the __init__ method is called. Now, I need a getter method to get self.__x3 by calling a.x3. How to achieve that in the pythonic way?
Attempting an answer based on the assumption that you want the __x# variables modified only during __init__, and never again, but also want the accessors to follow the same code path (possibly because the read is also programmatically complex):
In this case, you can have the implementing function take an additional, defaulted argument. When accessed in attribute form, it will receive the defaulted argument, but if the fget member of the property is explicitly accessed, it can be called with the non-default argument. A simple example addressing x1 only:
class A:
def __init__(self, x):
self.__x = x
# Access the property itself off the class, bypassing execution,
# then call it directly with the non-default argument
type(self).x1.fget(self, True)
#property
def x1(self, doset=False):
if doset:
self.__x1 = self.__x + 1
return self.__x1
Alternatively, to simplify the usage in __init__, you can use a separate name for the underlying function vs. the property to achieve the same effect:
class A:
def __init__(self, x):
self.__x = x
# Call the implementing function directly with the non-default argument
self._x1(True)
# Implementing function named with single underscore prefix to indicate it's
# for internal/protected use only
def _x1(self, doset=False):
if doset:
self.__x1 = self.__x + 1
return self.__x1
# Define property x1 based on x1 for outside use
x1 = property(_x1)
Of course, if you don't have a complicated getter path, then the real solution is to separate _x1 from x1 completely, where _x1 is pure setter helper function for __init__, and x1 is pure getter:
class A:
def __init__(self, x):
self.__x = x
# Call the init helper
self._init_x1()
# Implementing function named with single underscore prefix to indicate it's
# for internal/protected use only
def _init_x1(self):
self.__x1 = self.__x + 1
#property:
def x1(self):
return self.__x1
To be clear, only the last of these is "Pythonic" in any meaningful sense. The second option has some limited use cases (where you have a function that demands existence, and is highly configurable, but has a reasonable set of defaults that a property could use), but in that case, it's usually a function that has public utility just like the property. Option #1 is the least Pythonic, as it's inconvenient to use (needing to elevate to the class type, extract the fget member, and explicitly pass self), and makes it quite clear that there is no expected use case outside of __init__ (because it's such a pain to use that no one would bother).

Is it considered good practice to use **kwargs prolifically to aid readability?

When designing classes, I found it awkward to place default argument values in the __init__ method, as in:
class Class1(object):
def __init__(self, y=2, z=3):
self.y = self.manip_y(y)
self.z = self.manip_z(z)
def manip_y(self, y):
return y * 10
def manip_z(self, z):
return z - 30
Is it considered better practice to add **kwargs to function signatures to place default values in the function signatures as well?:
class Class2(object):
def __init__(self, **kwargs):
self.y = self.manip_y(**kwargs)
self.z = self.manip_z(**kwargs)
def manip_y(self, y=2, **kwargs):
return y * 10
def manip_z(self, z=3, **kwargs):
return z - 30
It's better to add default values in the __init__ signature -- that way someone only needs to look at the signature to figure out the options. And, in example 2, the default values are now hidden in other functions. Additionally, your documentation will be simpler.
do not do this. why? because it forces you to read not only the __init__ code to understand how to create the object but also all of the functions called therein.

Using python property and still able to set the values explicitliy

Im trying to understand how the #property decorator works.
Here I have used method y as a property for field x,
After the attribute-self.x has a property, does it mean that we can't set the value explicitly..
I thought the last statement--> c.x = 2 will not work once you have the property method set on a variable?
class C(object):
def __init__(self):
self.x = 0
self.list = [1,2,3,4,10]
#property
def y(self):
print 'getting'
self.x = sum(self.list)
return self.x
#y.setter
def y(self, value):
print 'setting'
self.x = value
if __name__ == '__main__':
c = C()
print 'Value of c.y=',c.y
print '-'*80
c.y = 50
print '-'*80
print c.y
print '-'*80
if c.y >5:
print 'Hi'
You can always set x explicitly.
class Foo(object):
def __init__(self):
self.x = 1
self.lst = [1,2,3]
#property
def y(self):
self.x = sum(self.lst)
return self.x
#y.setter
def y(self,value):
self.x = value
f = Foo()
print f.y #6
print f.x #6
f.x = 3
print f.x #3
print f.y #6
print f.x #6
The problem is that in this example, calling the getter (y) also sets the value of the x attribute, so you'll never see the change of x if you're doing all of the changing via y because the act of looking at y changes the value of x.
One way that you might try to get around that limitation is:
class Foo(object):
def __init__(self):
self.x = None
self.lst = [1,2,3]
#property
def y(self):
return sum(self.lst) if self.x is None else self.x
#y.setter
def y(self,value):
self.x = value
Now if you explicitly set a value for x (or y), that value will stick until you set it back to None which you could even do in another function decorated with #y.deleter if you really wanted.
There is limited support for private instance variables in Python via name-mangling
to avoid exposing x, you need two leading underscores, i.e. __x
You cant prohibit to change attribute directly using property decorator but You can do such a trick I think
class A(object):
def __init__(self):
self.x = 0
#property
def x(self):
return self.__dict__['x']
#x.setter
def x(self, value):
self.__dict__['x']=value
this will allow You to implement behavior like You have described
Python does not provide any capability for preventing callers from accessing variables. In other words, there is no "private" in Python. By convention, a variable or method prefixed with an underscore is not intended for external use. E.g.,
class C(object):
def __init__(self):
self._x = 0
self.list = [1,2,3,4,10]
.
.
.
I can still access _x if I really want to, and nothing prevents me from setting it.
>>> c = C()
>>> c._x
10
>>> c._x = 20
>>> c._x
20
However, by convention, the underscore tells me I'm doing something dangerous and ill advised. It's up to me, the programmer, to determine if I broke anything by doing it.
This is a conscious design decision made when creating Python. The idea is that whoever uses your class is responsible for what they do with it; if they misuse it and it breaks, that's their fault. You warned them with the underscore. I think the notion that a clever programmer can get around your attempts to lock them out anyway may have played a role in the decision (such as reflection libraries or interacting with the compiled bytecode directly), but don't hold me to that.
On a mildly related note, the underscore does actually do something if the variable (including other imported modules, functions, etc.) is a member of a module. Members beginning with an underscore are not imported by import *. E.g.,
a.py
_a = 10
b = 50
Command prompt:
>>> from a import *
>>> b
50
>>> _a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_a' is not defined
In your particular example, x and its setter are relatively useless since you're overriding its value any time the getter is called.
class Foo(object):
def init(self):
self.x = None
self.lst = [1,2,3]
#property
def y(self):
return sum(self.lst) if self.x is None else self.x
#y.setter
def y(self,value):
self.x = value

Python method call from class to class

I'm learning Python and I'm getting confused with syntax for calls from one class to another. I did a lot of search, but couldn't make any answer to work. I always get variations like:
TypeError: __init__() takes exactly 3 arguments (1 given)
Help much appreciated
import random
class Position(object):
'''
Initializes a position
'''
def __init__(self, x, y):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
class RectangularRoom(object):
'''
Limits for valid positions
'''
def __init__(self, width, height):
self.width = width
self.height = height
def getRandomPosition(self):
'''
Return a random position
inside the limits
'''
rX = random.randrange(0, self.width)
rY = random.randrange(0, self.height)
pos = Position(self, rX, rY)
# how do I instantiate Position with rX, rY?
room = RectangularRoom()
room.getRandomPosition()
You don't need to pass self - that is the newly created instance, and is automatically given by Python.
pos = Position(rX, rY)
Note that the error here is happening on this line, however:
room = RectangularRoom()
The issue on this line is you are not giving width or height.
Maybe the answers to these previous questions help you understand why python decided to add that explicit special first parameter on methods:
What is the purpose of self?
Why do you need explicitly have the "self" argument into a Python method?
Python - why use "self" in a class?
The error message may be a little cryptic but once you have seen it once or twice you know what to check:
Did you forgot to define a self/cls first argument on the method?
Are you passing all the required method arguments? (first one doesn't count)
Those expecting/given numbers help a lot.

Categories

Resources