On Sat, Nov 15, 2014 at 06:19:56PM +0000, Bo Morris wrote: > Thank you Alan and Danny. It amazes me at the lengths you guys, as well as > everyone else who contributes, will go to to help explain things to us; it > is greatly appreciated! > > Alan, I decided to dumb down the learning classes just a little. By > this I mean, I am not using Tkinter to learn classes. I am using one > of the examples from your website, which I did change it just a > little. I figured, I am having a hard time wrapping my head around > classes and Tkinter would just add to the confusion.
Good plan! Before I get to your example, let's dumb things down even more. What is the *simplest possible* class we can create? class MyClass(object): pass That's pretty simple. (Actually, we can make it even shorter by removing the word "object", but that makes it more complicated because it will work differently in Python 2 and 3, so let's not do that.) What do those two lines of code do? The header "class MyClass(object)" tells Python to define a new class called MyClass. The body is the line "pass", which is just a placeholder to tell Python that there's nothing else there. (Otherwise the compiler will complain.) MyClass inherits from "object". object is a special built-in name which Python already knows about. The "object" class defines some extremely basic behaviour, such as knowing how to print itself, and so our MyClass inherits that behaviour. Classes on their own usually aren't very interesting. There's not a lot of work you can do with them, normally you work with *instances*. The relationship between a class and its instances is similar to that between a kind of thing and a specific example of that thing. E.g.: class: Dog instances: Lassie, Rin-Tin-Tin, Snowy, Hooch, Ol' Yella class: President of the USA instances: Barrack Obama, George Bush Jr, George Washington class: Tool subclasses: Screwdriver, Spanner, Hammer, etc. instances: this red handled screwdriver, that 3" spanner etc. Instances get their behaviour from their class; their class get their behaviour either from code you program, or code they inherit from the superclasses. MyClass is a subclass of object, so object is a superclass of MyClass. I can create an instance of MyClass, and then print it: py> obj = MyClass() py> print(obj) <__main__.MyClass object at 0xb7b52f6c> How did `obj` know what to print when I never wrote any code to handle printing MyClass instances? It inherited that code from the superclass `object`, which already knows how to convert itself into a string, which print can then use: py> str(obj) '<__main__.MyClass object at 0xb7b52f6c>' So far, every MyClass instance is indistinguishable except by their "identity", their ID number which you can see written in hex in that string display or by calling id(): py> id(obj) 3082104684 py> hex(id(obj)) '0xb7b52f6c' [Aside: every single object in Python has an ID. It's usually going to be a cryptic multi-digit number, but some implementations will instead use an incrementing counter so that IDs will be 1, 2, 3, 4, ... ] We can associate some data with individual instances. That makes them distinguishable, and useful. What makes the ints 17 and 23 different is their *state*, that is the internal data (whatever that is!) which distinguishes the instance with value 17 from the instance with value 23. So far, our MyClass instances don't have any state, but we can give them some. Let's create a couple of instances of MyClass: py> a = MyClass() py> b = MyClass() py> a.colour = 'red' py> a.size = 23 py> b.colour = 'green' py> b.size = 42 py> if a.size >= b.size: ... print("the %s instance is bigger" % a.colour) ... else: ... print("the %s instance is bigger" % b.colour) ... the green instance is bigger The colour and size attributes count as examples of per-instance state. In a nutshell, that is what classes are all about: classes control the state of the instances, and define their behaviour. > So, I have the below code. When I run this from terminal, it obviously > prints "This is a test." If I may, break the code down and ask > questions as it pertains to the code? > > ################# > class Message: > def __init__(self, aString): > self.text = aString > > def printIt(self): > print self.text > > m = Message("This is a test") > m.printIt() > > ################## > > With the first part... > class Message: > def __init__(self, aString): > self.text = aString > > Will I always use "_init_" when defining the first function in a > class? I noticed on your website, you created a class where you did > not use "_init_" (see below). Was this because you did not define a > function? Note that there are *two* underscores __init__ not one _init_. Such "dunder" (Double leading and trailing UNDERscore) methods normally have special meaning to Python, or are reserved for future use. __init__ is one such special method. It is the "initialiser" method, and Python will automatically call it when you create a new instance, so as to initialise it. It is conventional to normally write it as the first method in the class, but the position doesn't matter, only the name. > class BalanceError(Exception): > value = "Sorry you only have $%6.2f in your account" This class doesn't contain an __init__ method because it just reuses the one defined by Exception, the superclass or parent class. Think of BalanceError being the child, when Python says to it "initialise a new instance!" it says "I don't know how to initialise instances, I'll ask my parent to do it -- Exception, initialise this instance for me!" > I noticed that I can change "text" to anything and I still get the > same results by running the code; I changed them to "blah" just as a > test. > > When I define a function in a class, will I always use "self" as the > first entry in the parenthesis? *Nearly* always. Functions inside classes are called "methods", and there are a couple of special purpose kinds of methods ("class methods" and "static methods") that don't use self, but don't worry about them for now. Ordinary methods will always use self. Technically, the name "self" is arbitrary. You could use anything you like, Python won't care. But it is a very strong convention to use the name self, and unless you have a really strong reason to change you should stick to that. > On the next part... > m = Message("This is a test") > m.printIt() > > I noticed I cannot run "printIt()" unless I make it an object i.e. > "m = Message("This is a test")...?" Correct. printIt is a method that belongs to the Message class. It isn't a global variable, so just trying to call printIt() on its own will fail, there's no name printIt defined. (Or perhaps there is, but it is something unexpected). This is useful because it separates unrelated pieces of code. Your Message class can define a printIt method to do what it needs it to do, without having to worry about another class coming along and defining a printIt method that does something completely different. Both strings and lists have an index method, and they do similar things, but work in different ways: py> "abcd xyz bcd bc".index("bc") 1 py> ["abcd", "xyz", "bcd", "bc"].index("bc") 3 there's no conflict because the code for string index() lives inside the str class and the code for list index() lives inside the list class. > I noticed I could change "m = Message("This is a test")" to "m = > Message(raw_input())," which works. Correct. Message() doesn't care where the argument comes from, it only sees the result, not the process of where it came from. All of these are the same: Message("hello") Message("HELLO".lower()) s = "hello"; Message(s) Message("h" + "e" + "l" + "l" + "o") although of course some might be a bit more efficient than others. > What if I wanted to create a function in Message that receives text > from another function and then prints that text instead of the text > from "m = Message("This is a test")...; can I pass or return values to > another function inside a class? Sure, no problem, methods can take arguments as well. class Example: def method(self, arg): print("Instance %r was sent the message 'method %r'" % (self, arg)) py> x = Example() py> x.method(42) Instance <__main__.Example object at 0xb7b420cc> was sent the message 'method 42' py> x.method('hello') Instance <__main__.Example object at 0xb7b420cc> was sent the message 'method 'hello'' py> x.method([]) Instance <__main__.Example object at 0xb7b420cc> was sent the message 'method []' But beware of writing methods that don't actually use self inside the method body. If you never use self, that's a sign that it doesn't need to be a method and should just be a top-level function outside of any classes. > The "self" is really throwing me off, `self` is the instance that you are using. When I write: x = Example() x.method([]) Python effectively translates that into: x = Example() Example.method(x, []) Look at the signature of method() -- the `self` argument gets x as its value, and the `arg` argument gets the list [] as its value. > when I think about creating different functions that do misc things > just to practice. For example, I have a function that kills a Linux > program. I just don't see how to rethink that function to where it > could be defined in a class? Ask yourself, what is the state that this class should manage? There really isn't any, is there? That's a good sign that a class with methods is not a great design for this. > def kill_proc(process1): > i = psutil.Popen(["ps", "cax"], stdout=PIPE) > for proc in psutil.process_iter(): > if proc.name(process1): > proc.kill() > > Would it be something like...? > class processKiller: > > def _init_(self): Since you don't have any data to attach to these ProcessKiller instances, you don't need to initialise them, so no __init__ method is needed. > > def kill_proc(self, process1): > i = psutil.Popen(["ps", "cax"], stdout=PIPE) > for proc in psutil.process_iter(): > if proc.name(process1): > proc.kill() > > Then outside of the class, call it like so...? > p = processKiller() > p.proc.kill() Correct, except that you forgot to pass the process ID to the kill_proc method. Which you also misspelled :-) Don't make the mistake of thinking that everything needs to be inside a class. Classes are a great solution when you have data and functions that need to be kept together. Here's a cautionary tale from the Land Of Java of what happens when language designers become too obsessed with putting everything inside classes: http://steve-yegge.blogspot.com.au/2006/03/execution-in-kingdom-of-nouns.html Unlike Java, Python is happy to mix and match functions and object methods as needed: result = len(mystring) - mystring.count("a") -- Steven _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor