In the class below, the self parameter in class method cost is replaced with another name 'insta' but still it works. Why?
class car():
model='sedan'
year=2016
price=775000
def cost(insta):
print "Price of car is: ",insta.price
c1=car()
c1.model='SUV'
c1.year=2017
c1.price=120000
c1.cost()
c2=car()
c2.model='hatchback'
c2.year=2018
c2.price=600000
c2.cost()
Naming self the first parameter of a class method is nothing more than a convention. Your code is strictly tantamount to:
def cost(self):
print "Price of car is: ",self.price
However, this convention is respected everywhere, and although I knew it was only a convention, I think it's the first time I see a code not naming the instance self.
When you instantiate c1 to be an object of class car, c1 gets those 3 things (attributes) you gave it: a model, a year, and a price. In order for c1 to know that you want its cost() method to access its price and not c2's price for example, you (correctly) defined the cost() method to associate the price printed with the price of the object that cost() is called on.
The word self is common, perhaps because given Python syntax, when you call a method on a class object with dot notation as in classObject.classMethod(), it might feel more like you are "calling classMethod() on classObject" than more typically "passing someArgument to someFunction" let's say, since you don't put classObject inside the parenthesis of classMethod. But the key piece is the association you make in the class definition (which will work with "self" or "insta" or "puppy", as long as you use the chosen word consistently). Perhaps a useful naming that would help illuminate what is going on in this particular context would be
def cost(myCar):
print ("Price of car is: ", myCar.price)
since you want the cost of myCar to be the price of myCar. This is what self does in a more general (and thus more readable and maintainable) way.
Related
In this example, am I violating LSP? Since straight up replacing the last two lines with an instance of a subclass will give me an error(as wage isn't initialised)?
person_1 = Employee('Brad')
person_1.print_name()
#dataclass
class Person:
name: str
def print_name(self):
print(self.name)
#dataclass
class Employee(Person):
wage: int
person_1 = Person('Brad')
person_1.print_name()
If so, then how can there ever be a non-violation of LSP when extending the constructor of a class (aside from placing optional attributes thereafter)?.
LSP says, that if something is true for a Person object (e.g. it has a name, the name is a string, it can print its name), it must be also true for an Employee object. In other words, every Employee is also a Person.
It does not state that an Employee object must be created the same way as a Person object. Every Employee is not only a Person. It has not only the name, but also a wage.
The second question:
If the Employee.print_name() method were redefined not to print the name, but for instance to return it as a string, that would break the principle.
Note that breaking the LSP does not require a code change, for instance if the Person's name format were changed from e.g. "first_name last_name" to "last_name, first_name" in the Employee, and that would cause the program to give incorrect output.
I know it's been answered already but i wanted to emphasize:
We need to differentiate between 2 relationships. One is the relationship between instances of Person and instances of Employee. The second one is the relationship between the 2 instances of type (The Person class itself, and the Employee class itself.)
In your case, LSP deals only with the former (everything that we can do with Person instances, we need to be able to do in the exact same way with Employee instances). It says nothing about the classes themselves.
Now, since python is very dynamic you could technically argue "Hey just wait second! There is something I can do with one and not the other!". Take a look at the following example:
# Assume we have an instance of either Person or Employee here
instance = _
# The following will work with Person instances but will raise an exception for Employee instances
instance_copy = type(instance)(instance.name)
I would say that you shouldn't count this kind of stuff. Let's call it an "unreasonable expectation of usage" that shouldn't be accounted for when considering the validity of your code structure in the vast vast majority of use cases.
The most important thing to remember is this:
B inherits from A != A (the class object itself) can be substituted by B (the class itself)
It depends on what you mean by the LSP.
Does it mean the strict LSP, like in Barbara Liskov's original paper, where the behaviour of the program should be unchanged by type substitution? (and even then it's a desired property, not an absolute requirement)
Or does it mean following the Person interface, in which case it would not be a violation, since you can't remove functions for the Person class in the Employee class? (Well, you technically could, but it's not a good idea to do that).
Say I have 2 classes: Water and Substance. one or more Substances can be dissolved in Water; a Water instance has a substances attribute containing a list of Substance instances. The diffusion constant of a Substance depends on the attributes of the Water it is dissolved in as well as some attributes of the Substance itself. Should I then make a get_diffusion_constant method on Water with an instance of Substance as its attribute, or should I add the method to Substance where Water is its argument? Or is there a different approach altogether?
You wrote:
The diffusion constant of a Substance depends on the attributes of the Water
This let think that the diffusion constant is a characteristic of the substance, so the Substance class should own the method that allows to compute it (with a parameter to provide a Water instance since it depends on it).
This works well for most simple case, if a real concepts owns a characteristic, its model (a class in this case) should own the related attribute or method.
Some design-patterns and/or more complex needs could justify to break this "rule" to introduce more astraction.
Additionally, to answer your comment: it is not a problem (IMO at least) to pass self to a function.
self is just a reference to the current instance and as nothing special except it is a widely respected convention for naming the current instance, and thus the first argument of (instance) methods.
To explain a bit more: an instance method must accept as first argument a reference to the related instance. By convention, this positional argument is named self, but you could decide to name it this, instance or whatever you want, it would be the same. Then you just would have to use the correct parameter name inside the method.
See the code below. It uses very bad names for the instance argument but it works as if self had been used:
class Foo:
def __init__(current_instance):
current_instance.bar = 'baz'
def __str__(this_name_is_ridiculous):
return this_name_is_ridiculous.bar
print(Foo()) # prints baz
I assume that Water inherits some stuff from Substance. So then you could have a diffusion_constant in each Substanceand also a diffuse function with takes one or more Substances
EDIT:
class Water:
def diffuse(self, *args):
#check if args is greater 0 or None and then iterate and apply diffusion_constants
class Substance:
diffusion_constant = 0 #base constant
class Sirup(Substance):
diffusion_constant = 3
#somewhere later then
corn_sirup = Sirup()
sugary_sirup = Sirup()
water = Water()
water.diffuse(corn_sirup, sugary_sirup)
EDIT:
Due to the comment I changed the code. Python has duck-typing, so as long your Substances have a diffusion_constant attribute, it is possible to access it, no matter what it is. This should then do it
i created this class for my homework:
class sayfa():
isim=" "
def __init__(self,bSayisi,ySayisi,pSayisi,iSayisi,tSayisi):
self.bSayisi=bSayisi
self.ySayisi=ySayisi
self.pSayisi=pSayisi
self.iSayisi=iSayisi
self.tSayisi=tSayisi
if ((((bSayisi+ySayisi+pSayisi)/iSayisi)/tSayisi)*100)>0.2:
print(isim,"başarılı")
else:
print(isim,"başarısız")
then i called it in another .py file:
from eRate import sayfa
ybs1=sayfa(365000,65000,870,500,1125000)
ybs1.isim="YBS-1"
then i tried to work it and it gave me this error:
NameError: name 'isim' is not defined
I think i did something wrong when i'm writing class but i don't know what i actually done wrong.Can you help me?
edit:
My code worked when i put isim variable in def init but it looks weird.It looks like this:
class sayfa():
def __init__(self,bSayisi,ySayisi,pSayisi,iSayisi,tSayisi,isim):
self.isim=str(isim)
self.bSayisi=bSayisi
self.ySayisi=ySayisi
self.pSayisi=pSayisi
self.iSayisi=iSayisi
self.tSayisi=tSayisi
if ((((bSayisi+ySayisi+pSayisi)/iSayisi)/tSayisi)*100)>0.2:
print(isim,"başarılı")
else:
print(isim,"başarısız")
and when i'm adding data in class it gets weirder:
from eRate import sayfa
ybs1=sayfa(365000,65000,870,500,1125000,"YBS-1")
The error isn't with the way you're assigning things, but with the way you're accessing them.
Just as you have to do self.bSayisi to set an attribute, you have to do self.isim to access one. So:
print(self.isim, "başarılı")
(and the same for the other line…)
If you're wondering why you were able to access other values like bSayisi without self.bSayisi—that's just because you happen to have a parameter named bSayisi that happens to have the same value as self.bSayisi (because you just made that true a few lines earlier). If you changed it to, say, self.bSayisi = bSayisi*2, or you renamed the parameter to myBSayisi and did self.bSayisi = myBSayisi, you'd see that just using bSayisi instead of self.bSayisi was no longer correct.
However, while this eliminates the error, I'm not sure it actually does what you want. At the time you're doing this print, you haven't assigned an isim value to the object yet, so it's going to get the class value as a default, so it's always just going to be " ". Is that really what you wanted?
If not, you need to move the print calls to some other method that you can call later, after having assigned isim. For example:
class sayfa():
isim=" "
def __init__(self,bSayisi,ySayisi,pSayisi,iSayisi,tSayisi):
self.bSayisi=bSayisi
self.ySayisi=ySayisi
self.pSayisi=pSayisi
self.iSayisi=iSayisi
self.tSayisi=tSayisi
def displaystuff(self):
if ((((self.bSayisi+self.ySayisi+self.pSayisi)/self.iSayisi)/self.tSayisi)*100)>0.2:
print(self.isim,"başarılı")
else:
print(self.isim,"başarısız")
ybs1=sayfa(365000,65000,870,500,1125000)
ybs1.isim="YBS-1"
ybs1.displaystuff()
Of course moving the isim into the constructor works, by avoiding the problem you were running into. It's not an answer to how to add data after the __init__ method, of course, because you're instead adding the data in the __init__ method. When that's appropriate, it's the simplest answer.
But if it looks weird in this case (I'll take your word for it; I don't know exactly what this code is trying to do), it's probably the wrong answer for this particular class.
In which case, you do need to know how to add data after the __init__ method, as you asked. Or, rather, you need to know how to access that data—because you were already adding it correctly.
This is the difference between class attributes (when it is outside of the __init__ with no self.) and instance attributes (when you added it inside the __init__ with the self.).
Class attributes are a little more complicated since they pertain to all the instances of that class (you could overwrite them within some instances, but then they'd become instance attributes in those cases)... and so if you changed a class attribute, it would affect all other instances you may have created or will create in the future.
For a more in-depth discussion of class attributes vs instance attributes see this answer that summarizes this post.
Normall __init__(..) is used to initialize / instantiate your instance. I would not print in it, nor calculate (unless you calculate some other class-variables and set them).
You need to prefix your variables of the instance by self. and the static class variable with the class name to acess it:
class sayfa():
isim=" " # this is a shared class variabl (aka static)
def __init__(self,bSayisi,ySayisi,pSayisi,iSayisi,tSayisi):
self.bSayisi=bSayisi # these are all instance variables, not shared
self.ySayisi=ySayisi
self.pSayisi=pSayisi
self.iSayisi=iSayisi
self.tSayisi=tSayisi
self.unusedSum = ySayisi + pSayisi + iSayisi
def printMe(self): # lookup __str__() and __repr__() for how to output your instance
if ((((self.bSayisi+self.ySayisi+self.pSayisi)/self.iSayisi)/self.tSayisi)*100)>0.2:
print(sayfa.isim,"some text") # use the static class variable
else:
print(sayfa.isim,"some other text")
sayfa.isim = "Coffee " # you set static class variables by prefixing class name
my_sayfa_instance = sayfa(365000,65000,870,500,1125000)
other_sayfa_instance = sayfa(3600,65000,870,500,10)
my_sayfa_instance.printMe()
other_sayfa_instance.printMe()
Output:
Coffee some other text
Coffee some text
I am learning Python, and I'm trying to simulate a card game.
I have a question regarding dot notation. I have looked all over the internet for a specific answer but was unable to find one.
Why is it, that sometimes we are taught to call a method in dot notation like this:
object.methodName()
While other times we are shown to call it like this:
className.methodName(object)
What is the difference?
Here is a specific example. This is a method definition from the book (How to think like a Computer Scientist)
class Hand(Deck):
def __init__ (self, name = " "):
self.cards = []
self.name = name
def __str__(self):
s = "Hand "+ self.name
if self.isEmpty():
return s+" is empty\n"
else:
return s+ " contains\n" + Deck.__str__(self)
Sometimes the object comes before the method:
self.isEmpty()
Sometimes the object comes after the method, in the parenthesis:
Deck.__str__(self)
Which method do we use? Does it matter?
Sometimes the object comes after the method, in the parenthesis:
Deck.__str__(self)
No, the object always comes before its own method. There are two python "objects" in this line. You are passing the self object (which is really a class instance) as a parameter to the __str__ method of the Deck object (which is really a class).
Wenn you use:
object.methodName()
then you calling the method of a special instance with it's own data.
As the method is related to the given instance you get different results.
a = Hand()
b = Hand()
then
a.cards
b.cards
may be different (in most cases).
To access this data the object self has to put in every method.
This is done by:
def foo(self):
return self.cards
When you define the method.
Unless you don't write a special class method than you always have to put the self there.
When calling object.methodName() the method automatically gets the information of the object it belongs to (because you hand the object/instance reference over before the dot. This information is stored in the parameter self. Thus the method can for example do: return self.cards
But if you call it like Deck.__str__(self) than you only have the class this means there is no self (which is the object reference itself).
So the method does not know which instance data it belongs to, therefore you have to hand over the self.
So summed up, if you call:
object.__str__() the information about the instance is given because you used the object reference to call the method.
But if you use Deck resp. the className than you have to put the information about the instance also via className.method(self) because otherwise there is no object reference for an object instance.
Generally, the object comes first.
Like this:
object.methodName()
Or like this:
self.isEmpty()
But I wanted to know why my Python book is teaching me to call this one method like this:
Deck.__str__(self)
I wanted to know why "self" is in parentheses at the end, instead of at the beginning of the method. In other words, why not write it like this:
self.Deck._str_()
The reason, is that the code above would throw an error:
"Hand instance has no attribute Deck
The above error is caused because written this way, Python thinks that "Deck" is an attribute of self. But in this case it is not. Deck, is the PARENT type to the Hand type, it is not an attribute of self.
Here is the code block where you can see the code in question, as well as the parent type (Deck) and the child type I am working with (Hand). In this code, we are writing a method to overwrite the string formatting for Hand, by accessing the string formatting in Deck. The code snippet Deck.__str__(self)
is written this way because we need a way to tell Python which type object we would like it to copy the string formatting from. In this case, we wanted the string formatting from Deck, the parent.
class Hand(Deck):
def __init__ (self, name = " "):
self.cards = []
self.name = name
def __str__(self):
s = "Hand "+ self.name
if self.isEmpty():
return s+" is empty\n"
else:
return s+ " contains\n" + Deck.__str__(self)
Also, as stated by # Martijn Pieters, using SUPER: super().__str__() is better than doing it the way I did: Deck.__str__(self)
We have not yet learned Super, but you can learn more about it here(https://docs.python.org/2/library/functions.html#super). Super basically tells Python to find the parent formatting for you, and use that. Much easier!
I hope that this explanation is understandable. I tried to over-explain to make up for anything that is not clear :-/
Is there any difference in the following two pieces of code? If not, is one preferred over the other? Why would we be allowed to create class attributes dynamically?
Snippet 1
class Test(object):
def setClassAttribute(self):
Test.classAttribute = "Class Attribute"
Test().setClassAttribute()
Snippet 2
class Test(object):
classAttribute = "Class Attribute"
Test()
First, setting a class attribute on an instance method is a weird thing to do. And ignoring the self parameter and going right to Test is another weird thing to do, unless you specifically want all subclasses to share a single value.*
* If you did specifically want all subclasses to share a single value, I'd make it a #staticmethod with no params (and set it on Test). But in that case it isn't even really being used as a class attribute, and might work better as a module global, with a free function to set it.
So, even if you wanted to go with the first version, I'd write it like this:
class Test(object):
#classmethod
def setClassAttribute(cls):
cls.classAttribute = "Class Attribute"
Test.setClassAttribute()
However, all that being said, I think the second is far more pythonic. Here are the considerations:
In general, getters and setters are strongly discouraged in Python.
The first one leaves a gap during which the class exists but has no attribute.
Simple is better than complex.
The one thing to keep in mind is that part of the reason getters and setters are unnecessary in Python is that you can always replace an attribute with a #property if you later need it to be computed, validated, etc. With a class attribute, that's not quite as perfect a solution—but it's usually good enough.
One last thing: class attributes (and class methods, except for alternate constructor) are often a sign of a non-pythonic design at a higher level. Not always, of course, but often enough that it's worth explaining out loud why you think you need a class attribute and making sure it makes sense. (And if you've ever programmed in a language whose idioms make extensive use of class attributes—especially if it's Java—go find someone who's never used Java and try to explain it to him.)
It's more natural to do it like #2, but notice that they do different things. With #2, the class always has the attribute. With #1, it won't have the attribute until you call setClassAttribute.
You asked, "Why would we be allowed to create class attributes dynamically?" With Python, the question often is not "why would we be allowed to", but "why should we be prevented?" A class is an object like any other, it has attributes. Objects (generally) can get new attributes at any time. There's no reason to make a class be an exception to that rule.
I think #2 feels more natural. #1's implementation means that the attribute doesn't get set until an actual instance of the class gets created, which to me seems counterintuitive to what a class attribute (vs. object attribute) should be.