Python: Same name for class method parameters and class attribute - python

I have an assignment on classes. One of my tasks is as follows:
a. Augment the Tribute class by adding a new property, hunger, which will describe
the level of hunger for the Tribute. The initial value for hunger should be 0, as all
the Tributes will start the game with full stomach.
b. Create a method, get_hunger(), which return the current hunger level of the tribute.
c. Create a method, add_hunger(hunger), which will add a hunger value to the Tribute’s
hunger. When the hunger of a Tribute is equal or more than 100, he/she will
go_to_heaven(). (FYI go_to_heaven() is defined previously by other parent classes)
1)I wrote the following code, and when I tried running it I keep getting syntax error highlighted on the indentation right before self.get_hunger()+=hunger. May I know the reason for the syntax error since .get_hunger() is essentially self.hunger. self.get_hunger()=0 will work for other codes following this task but I don’t understand why self.get_hunger()+=hunger wont work. My lecturer stresses on not breaking the underlying layer of abstraction, which is why I would use the method .get_hunger() over attribute hunger, especially if I needed to get hunger value from instances of future child classes of Tribute, not sure if this concept is also embraced in practical situations.
class Tribute(Person):
def __init__(self, name, health):
super().__init__(name, health, -1)
self.hunger=0
def get_hunger(self):
return self.hunger
def add_hunger(self,hunger):
self.get_hunger()+=hunger #dk why can't assign to function call
if self.get_hunger()>=100:
self.go_to_heaven()
2)I also tried writing self.hunger+=hungerinstead of self.get_hunger()+=hunger to get past the syntax error and it works.However, I don’t find it intuitive why when defining a class method, and when I face a scenario where the name of the method parameter and the name of the class attribute is the same, the parameter will not overwrite the attribute in the form of hunger. Can anyone reason with me?

Assignments are performed on variables. That's just how Python works. Variables are references to objects in memory.
Function calls return objects, and you can't assign to an object.
I recommend using a setter method to handle the other side of the abstraction.
class Tribute(Person):
...
def get_hunger(self):
return self.hunger
def set_hunger(self, hunger):
self.hunger = hunger
def add_hunger(self,hunger):
self.set_hunger(self.get_hunger() + hunger)
if self.get_hunger() >= 100:
self.go_to_heaven()

Looks like you have abstraction already, since you're using a method to increase class field add_hunger() with health checking inside. Not using class field directly inside it's own method doesn't seem to have much sense.
You can't access class field self.hunger by using its method self.get_hunger().
Your method self.get_hunger() returns value of self.hunger (its copy), but not the variable itself. So you can add any number to that value, but you need to write it somewhere to keep its value. So, when you run self.get_hunger()+=hunger your method returns a copy of self.hunger, adds hunger from parameters to it and then this copy is lost, but self.hunger is the same.
So, if you want to increase self.hunger - you just need to use self.hunger+=hunger, which you checked already.
It would actually work if you would use the type of variable, that is passed by reference, not by value. Like list in this example, but I'd say it's kind of a perverted way to do so. ;)
class Tribute(Person):
def __init__(self, name, health):
super().__init__(name, health, -1)
self.hunger=[0]
def get_hunger(self):
return self.hunger
def add_hunger(self,hunger):
self.get_hunger()[0]+=hunger # dk why can't assign to function call
if self.get_hunger()[0]>=100:
self.go_to_heaven()
Using the same names for different things is not good. It can cause some errors. Even if one of them is variable and another one is method. If you try to pass that method to some thread later - it will never know which one you're passing there. If all names are different - it's safe and more readable.

Related

Changing Python class methods after class definition–is it harmful/harmless/useful/useless?

I found out that it is possible to assign to class variables after class definitions and that methods are technically class variables. So I tried the following, which appeared to work.
class Fruit():
def __init__(self, name, price):
self.name = name
self.price = price
a = Fruit('apple', 5)
Fruit.__init__ = lambda self: None
b = Fruit()
Can something like this potentially break things? On the other hand, is there a practical situation where this can be useful?
Of course it can and will break things. Any other code that tries to initialize a Fruit with a name and a price will now raise an exception, as the replaced constructor doesn't accept those parameters.
In general, the only practical situation is mocking/patching for tests (or certain, very rare runtime cases where there is no other way). However, that patching is best done with a library to deal with it, e.g. the standard library's unittest.mock.
It is very dangerous to have direct access to class instances for many reasons.
1- Changing the Name of an Instance Variable
The first problem with direct access is that changing the name of an
instance variable will break any client code that uses the original name directly. if the developer changes the name of an instance
variable in the class from self.originalName to self.newName, then any client software that uses the original name directly will break.
2- Changing an Instance Variable into a Calculation
A second situation where direct access is problematic is when the code of
a class needs to change to meet new requirements. Suppose that when
writing a class, you use an instance variable to represent a piece of data,
but the functionality changes so that you need an algorithm to compute a
value instead.
3- Validating Data
The third reason to avoid direct access when setting a value is that client
code can too easily set an instance variable to an invalid value. A better
approach is to call a method in the class, whose job is to set the value. As
the developer, you can include validation code in that method to ensure
that the value being set is appropriate.
So, it is always better to use getters and setters methods just in case an instance variable needs to be accessed from outside the class.
But, there are certain circumstances where it is safe to use direct access: when it is absolutely clear what the instance variable means, little or no validation of the data is needed, and there is no
chance that the name will ever change. A good example of this is the Rect
(rectangle) class in the pygame package. A rectangle in pygame is defined
using four values—x, y, width, and height—like this:
oRectangle = pygame.Rect(10, 20, 300, 300)
After creating that rectangle object, using oRectangle.x, oRectangle.y,
oRectangle.width, and oRectangle.height directly as variables seems acceptable
Source: Object-oriented python by Irv Kalb

Python: must __init__(self, foo) always be followed by self.foo = foo?

I've been striving mightily for three days to wrap my head around __init__ and "self", starting at Learn Python the Hard Way exercise 42, and moving on to read parts of the Python documentation, Alan Gauld's chapter on Object-Oriented Programming, Stack threads like this one on "self", and this one, and frankly, I'm getting ready to hit myself in the face with a brick until I pass out.
That being said, I've noticed a really common convention in initial __init__ definitions, which is to follow up with (self, foo) and then immediately declare, within that definition, that self.foo = foo.
From LPTHW, ex42:
class Game(object):
def __init__(self, start):
self.quips = ["a list", "of phrases", "here"]
self.start = start
From Alan Gauld:
def __init__(self,val): self.val = val
I'm in that horrible space where I can see that there's just One Big Thing I'm not getting, and I it's remaining opaque no matter how much I read about it and try to figure it out. Maybe if somebody can explain this little bit of consistency to me, the light will turn on. Is this because we need to say that "foo," the variable, will always be equal to the (foo) parameter, which is itself contained in the "self" parameter that's automatically assigned to the def it's attached to?
You might want to study up on object-oriented programming.
Loosely speaking, when you say
class Game(object):
def __init__(self, start):
self.start = start
you're saying:
I have a type of "thing" named Game
Whenever a new Game is created, it will demand me for some extra piece of information, start. (This is because the Game's initializer, named __init__, asks for this information.)
The initializer (also referred to as the "constructor", although that's a slight misnomer) needs to know which object (which was created just a moment ago) it's initializing. That's the first parameter -- which is usually called self by convention (but which you could call anything else...).
The game probably needs to remember what the start I gave it was. So it stores this information "inside" itself, by creating an instance variable also named start (nothing special, it's just whatever name you want), and assigning the value of the start parameter to the start variable.
If it doesn't store the value of the parameter, it won't have that informatoin available for later use.
Hope this explains what's happening.
I'm not quite sure what you're missing, so let me hit some basic items.
There are two "special" intialization names in a Python class object, one that is relatively rare for users to worry about, called __new__, and one that is much more usual, called __init__.
When you invoke a class-object constructor, e.g. (based on your example) x = Game(args), this first calls Game.__new__ to obtain memory in which to hold the object, and then Game.__init__ to fill in that memory. Most of the time, you can allow the underlying object.__new__ to allocate the memory, and you just need to fill it in. (You can use your own allocator for special weird rare cases like objects that never change and may share identities, the way ordinary integers do for instance. It's also for "metaclasses" that do weird stuff. But that's all a topic for much later.)
Your Game.__init__ function is called with "all the arguments to the constructor" plus one stashed in the front, which is the memory allocated for that object itself. (For "ordinary" objects that's mostly a dictionary of "attributes", plus the magic glue for classes, but for objects with __slots__ the attributes dictionary is omitted.) Naming that first argument self is just a convention—but don't violate it, people will hate you if you do. :-)
There's nothing that requires you to save all the arguments to the constructor. You can set any or all instance attributes you like:
class Weird(object):
def __init__(self, required_arg1, required_arg2, optional_arg3 = 'spam'):
self.irrelevant = False
def __str__(self):
...
The thing is that a Weird() instance is pretty useless after initialization, because you're required to pass two arguments that are simply thrown away, and given a third optional argument that is also thrown away:
x = Weird(42, 0.0, 'maybe')
The only point in requiring those thrown-away arguments is for future expansion, as it were (you might have these unused fields during early development). So if you're not immediately using and/or saving arguments to __init__, something is definitely weird in Weird.
Incidentally, the only reason for using (object) in the class definition is to indicate to Python 2.x that this is a "new-style" class (as distinguished from very-old-Python "instance only" classes). But it's generally best to use it—it makes what I said above about object.__new__ true, for instance :-) —until Python 3, where the old-style stuff is gone entirely.
Parameter names should be meaningful, to convey the role they play in the function/method or some information about their content.
You can see parameters of constructors to be even more important because they are often required for the working of the new instance and contain information which is needed in other methods of the class as well.
Imagine you have a Game class which accepts a playerList.
class Game:
def __init__(self, playerList):
self.playerList = playerList # or self.players = playerList
def printPlayerList(self):
print self.playerList # or print self.players
This list is needed in various methods of the class. Hence it makes sense to assign it to self.playerList. You could also assign it to self.players, whatever you feel more comfortable with and you think is understandable. But if you don't assign it to self.<somename> it won't be accessible in other methods.
So there is nothing special about how to name parameters/attributes/etc (there are some special class methods though), but using meaningful names makes the code easier to understand. Or would you understand the meaning of the above class if you had:
class G:
def __init__(self, x):
self.y = x
def ppl(self):
print self.y
? :) It does exactly the same but is harder to understand...

Placing custom class object in a list

I'm fairly new to object oriented programming so some of the abstraction ideas are a little blurry to me. I'm writing an interpreter for an old game language. Part of this has made me need to implement custom types from said language and place them on a stack to be manipulated as needed.
Now, I can put a string on a list. I can put a number on a list, and I've even found I can put symbols on a list. But I'm a bit fuzzy on how I would put a custom object instance on a list when I can't just drop it into a variable (since, after all, I don't know how many there will be and can't go about defining them by hand while the code is running :)
I've made a class for one of the simplest data types-- a DBREF. The DBREF just contains a Database reference number. I can't just use an integer, string, dictionary, etc, because there are type-checking mechanisms in the language I have to implement and that would confuse matters, since those are already used elsewhere in their closes analogues.
Here is my code and my reasoning behind it:
class dbref:
dbnumber=0
def __init__(self, number):
global number
dbnumber=number
def getdbref:
global number
return number
I create a class named dbref. All it does (for now) is take a number and store it in a variable. My hope is that if I were to do:
examplelist=[ dbref(5) ]
That the dbref object would be on the stack. Is that possible? Further, will I be able to do:
if typeof(examplelist[0]) is dbref:
print "It's a DBREF."
else:
print "Nope."
...or am I misunderstanding how Python classes work? Also, is my class definition wonky in any way?
If you used...
class dbref:
dbnumber=0
that would share the same number among all instances of the class, because dbnumber would be a class attribute, rather than an instance attribute. Try this instead:
class dbref(object):
def __init__(self, number):
self.dbnumber = number
def getdbref(self):
return self.dbnumber
self is a reference to the object instance itself that's automatically passed by Python when you call one of the instance's methods.

Is it necessary to include __init__ as the first function every time in a class in Python?

In Python, I want to know if it is necessary to include __init__ as the first method while creating a class, as in the example below:
class ExampleClass:
def __init__(self, some_message):
self.message = some_message
print "New Class instance created, with message:"
print self.message
Also, why do we use self to call methods?
Can someone explain the use of "self" in detail?
Also, why do we use pass statement in Python?
No, it isn't necessary.
For example.
class A(object):
def f():
print 'foo'
And you can of course use it, in this manner:
a = A()
a.f()
In fact you can even define a class in this manner.
class A:
pass
However, defining __init__ is a common practice because instances of a class usually store some sort of state information or data and the methods of the class offer a way to manipulate or do something with that state information or data. __init__ allows us to initialize this state information or data while creating an instance of the class.
Here is a complete example.
class BankAccount(object):
def __init__(self, deposit):
self.amount = deposit
def withdraw(self, amount):
self.amount -= amount
def deposit(self, amount):
self.amount += amount
def balance(self):
return self.amount
# Let me create an instance of 'BankAccount' class with the initial
# balance as $2000.
myAccount = BankAccount(2000)
# Let me check if the balance is right.
print myAccount.balance()
# Let me deposit my salary
myAccount.deposit(10000)
# Let me withdraw some money to buy dinner.
myAccount.withdraw(15)
# What's the balance left?
print myAccount.balance()
An instance of the class is always passed as the first argument to a method of the class. For example if there is class A and you have an instance a = A(), whenever you call a.foo(x, y), Python calls foo(a, x, y) of class A automatically. (Note the first argument.) By convention, we name this first argument as self.
In addition to other answers, one point in your question that has not been addressed :
Is it necessary to include __init__ as the first function
everytime in a class in Python?
The answer is no. In the case you need a constructor, it can be located at any position of your code, although the conventional and logical place is the beginning.
You don't need to put it in your Class; it is the object constructor.
You will need it if you want things to happen automatically to your object when it is instantiated.
No, it is not necessary to use the init in a class. It's a object constructor that define default values upon calling the class.
If you're programming in OOP manner and ought to have a basic structure of your class. You often will need this.
I read your other sub-question regarding
Can u explain about the use of "self"??? – harsh Jul 28 '11 at 5:13
Please refer to this post in stackoverflow. There's a lot of useful links to help you better understand python's init function.
Python __init__ and self what do they do?
Is not necessary the "init" statement, besides the "pass" statement is just used for skip, either to the next part of the code, or just skip cause was reached a special part like an "if" statement.
I initially struggled with that question too then I realize it is just another way to store certain data to your object and that data can be passed to any object method you define since your instance method has a self argument that can point back to the data you created in the init method.
No, it is not necessary but it helps in so many ways. people from Java or OOPS background understand better.
For every class instance, there is an object chaining that needs to complete when we instantiate any class by creating an object.
If we don’t put it compiler/interpreter puts it. But when we need some action to be formed while creating an object then we must have to pass it.
first.py
-------
class A:
def one(self):
print("something")
second.py
----------
from first import A
class B:
def test(self):
a = A()
x = a.one()
print(x)
test(any)
**output:**
something
None
Sure that this not required.
Please read more about defining python classes in this tutorial here.
Read more about __init__ in the documentation here and at What do __init__ and self do in Python?.
In general __init__ is a kind of constructor that is called automatically and allows you to perform any additional actions(adding variables, calling any methods and so on - the idea is to have the ability to initialize instance since it is already created and now you may need to do something with it before proceeding - for example remember creation time or serializing its initial state and so on) while creating object. So if you don't need to do some special preparation you may skip using it.
Its not necessary...
It's just function that runs everytime an object is created from your class...
And it can be helpful if you want every object have some things in common

Why do new instances of a class share members with other instances?

class Ball:
a = []
def __init__(self):
pass
def add(self,thing):
self.a.append(thing)
def size(self):
print len(self.a)
for i in range(3):
foo = Ball()
foo.add(1)
foo.add(2)
foo.size()
I would expect a return of :
2
2
2
But I get :
2
4
6
Why is this? I've found that by doing a=[] in the init, I can route around this behavior, but I'm less than clear why.
doh
I just figured out why.
In the above case, the a is a class attribute, not a data attribute - those are shared by all Balls(). Commenting out the a=[] and placing it into the init block means that it's a data attribute instead. (And, I couldn't access it then with foo.a, which I shouldn't do anyhow.) It seems like the class attributes act like static attributes of the class, they're shared by all instances.
Whoa.
One question though : CodeCompletion sucks like this. In the foo class, I can't do self.(variable), because it's not being defined automatically - it's being defined by a function. Can I define a class variable and replace it with a data variable?
What you probably want to do is:
class Ball:
def __init__(self):
self.a = []
If you use just a = [], it creates a local variable in the __init__ function, which disappears when the function returns. Assigning to self.a makes it an instance variable which is what you're after.
For a semi-related gotcha, see how you can change the value of default parameters for future callers.
"Can I define a class variable and replace it with a data variable?"
No. They're separate things. A class variable exists precisely once -- in the class.
You could -- to finesse code completion -- start with some class variables and then delete those lines of code after you've written your class. But every time you forget to do that nothing good will happen.
Better is to try a different IDE. Komodo Edit's code completions seem to be sensible.
If you have so many variables with such long names that code completion is actually helpful, perhaps you should make your classes smaller or use shorter names. Seriously.
I find that when you get to a place where code completion is more helpful than annoying, you've exceeded the "keep it all in my brain" complexity threshold. If the class won't fit in my brain, it's too complex.

Categories

Resources