I believe there's several people on PD learning python (Faust, BDS), apparently Google's tutorials are pretty good:
http://blog.hartleybrody.com/2012/01/google-python/
http://sumukh.me/GFM5+ (PDF that's linked on the blog, in case it's down)
Oh and since I know quite a bit about the subject, if you got any questions, post and I'll try to answer them here.
Thank you for that. Actually quickest link to execution ever, quite rewarding. BASIC was hard by comparison. A friend of mine was talking about "completion" w.r.t. some language/code - is that anything worth trying?
Not sure what you mean by "completion"? You mean that, when in the editor, writing your code, it'll automatically complete/predict often-used words for you?
Then yes, that's definitely worth getting. Another great thing to have is "syntax highlighting", which gives different colours to different things in your code, variables, keywords, strings, numbers, etc.
If you're on Windows, PyScripter (https://www.google.com/search?q=pyscripter&btnI=yes) is by far your best option for a code editor. An advanced one like this is actually not just called editor, but IDE (Integrated Development Environment). Apart from syntax highlighting (which is pretty standard in about any editor with more features than Notepad), it can do code-completion, it'll even give you a pop-up drop-down menu with the methods and properties of an object as soon as you press the '.', and when you call a function or method it'll show a tooltip with the parameters when you type the opening '(' so you can easily know what parameters to pass. It also has a built-in interactive Python interpreter that sort of runs together with your code, as well as a shortcut key to paste+execute currently selected block of code in that interpreter, and it has a debugging environment in which you can set breakpoints or manually advance step-by-step through your code running and in the meanwhile you can change stuff via the interpreter or inspect variables and such via another window. And IMO the bestest thing is, if you hold Ctrl (or maybe it was Shift) every variable, class or identifier becomes a hyperlink when you mouse over it and you can click and it'll jump straight to where it's defined in the source code, even in another tab or automatically opening the file if it's not open yet, and it even works with (non-binary) external python modules/libraries such as Django, in which sense it's sometimes more useful to check the source code and function definitions of what you're calling than figuring it out from the module's documentation.
Other more general purpose code-editors are PSPad (http://www.pspad.com/) (windows only) and jEdit (http://jedit.org/) (Java-based, cross-platform). They do syntax highlighting for a great many types of files (HTML, XML, Java, C, Python, PHP, you name it. Both have about 100 syntaxes built-in). They can both do code-completion, though it requires a bit fiddling to get it working, plus since they're not Python-specific, they're not as advanced as PyScripter. Be sure to also check jEdit's many plugins (you can just select and install them from the menu).
And even if you're already decided on PyScripter it's still good to have one of these general-purpose editors around, since they're just really powerful for a lot of heavy-duty text-editing needs.
If you're on Linux, I personally like to use gEdit, since it's really lightweight, but can be made to do all sorts of amazing things if you install the right plugins. Hard-core Linux coders seem to generally prefer Vim or Emacs, though. Vim's got a very steep learning curve because it operates rather differently than any text-editor you're probably used to, but there's a load of tutorials out there, especially since for some reason last year it got a surge in popularity. Emacs is easier to use right away, but it's huuuuuuuuge. Both Emacs and Vim can literally do anything.
Also for Linux, I can really recommend IPython. That's a python application designed to replace the default interactive Python interpreter, and it can do all sorts of amazing things. Especially great if you use it with PyLab/Matplotlib for doing calculations and plotting graphs and such--those things combined basically provide you with a free, open source version of MATLAB, except you're coding in Python, not MATLAB scripts, which is nice.
You can probably also get IPython to run on Windows, but I doubt you'd be using it much when you also got PyScripter ... Not running Windows myself, PyScripter, Ableton Live and uTorrent are probably the only three programs I regret not having access to on Linux (there's good torrent clients for Linux of course, but uTorrent is just really good and lightweight) (also yes I know I can use Wine).
I learned a little Python a couple of years ago, and then got distracted by other things. I'd like to get back into it, but I've still got these other things that I want to do more.
Some day 03.
Some day.
Yea, I,m not sure what it (completion) means. Along similar lines to auto-complete, I think he was mentioning that the code was generative to the extent of writing itself on a higher level of complexity than just auto-complete. Again, something that sounds unreasonable, but maybe it was worth a shot... Meanwhile, python is super cool, the interactive interpreter mode is awesome, really decisive design feature: the idea of interacting-processing should be built right into the ground :fnord:
Yea, I like e-macs but the interface is a little culty, that and once I got stuck in those buffers and was traumatized. So now I use nano - but I like the idea of hyperlinking variables... I used to use Borlands compiler in Dos, and color coding the functions made the experience more friendly, maybe I'll get one - for now I'm just grooving on not needing one!
For related audio visual applications I was thinking of using it to code things in "processing" and "node box" down the line.
As for how you can live without ableton.... :eek:
Quote from: LuciferX on January 28, 2012, 12:31:26 AM
Yea, I,m not sure what it (completion) means. Along similar lines to auto-complete, I think he was mentioning that the code was generative to the extent of writing itself on a higher level of complexity than just auto-complete. Again, something that sounds unreasonable, but maybe it was worth a shot...
Are you into generative art, by any chance?
QuoteMeanwhile, python is super cool, the interactive interpreter mode is awesome, really decisive design feature: the idea of interacting-processing should be built right into the ground :fnord:
In that case, get IPython right now! It's a replacement for the interactive interpreter and it's simply just better. Better keyboard editing, it remembers all your commands, got colours, it even remembers the *results* of all your commands in a global array of objects.
Additionally the latest IPython has a "QT Console" which is the interactive interpreter in its own GUI window. It's still a command line, just like a terminal, but it can do extra amazing shit. For example with Pylab/Matplotlib it can display your plots inline as graphics. You can of course also define your own objects to have a graphical representation. It's similar to how all Python objects have a
__str__ and a
__repr__ method for string representation.
QuoteYea, I like e-macs but the interface is a little culty, that and once I got stuck in those buffers and was traumatized. So now I use nano - but I like the idea of hyperlinking variables... I used to use Borlands compiler in Dos, and color coding the functions made the experience more friendly, maybe I'll get one - for now I'm just grooving on not needing one!
Hm, my version of Nano also does syntax highlighting btw. You might want to look into that.
Still, if you're on Linux, better to use GEdit (if on Gnome) or Kate (if on KDE*). Unless for some reason you're restricted to a text mode interface? Because in that case, if I had to do that a lot, I'd get real proficient in Vim, real fast. I'm pretty good at it already, but not enough to use it instead of a regular editor.
(*hadn't mentioned Kate yet because I haven't used it in a while, but 5 years back it was really awesome, and as open source goes, it probably only got better)
QuoteFor related audio visual applications I was thinking of using it to code things in "processing" and "node box" down the line.
Processing (http://en.wikipedia.org/wiki/Processing_(programming_language)) is cool (it's a framework/language for algorithmic art--for those reading along). Haven't heard of node box.
QuoteAs for how you can live without ableton.... :eek:
I KNOW, RIGHT>?!!
srsly if you know any decent open source linux audio editor that can zoom and pan like Ableton, cut-n-paste clips like Ableton, design effect envelopes like Ableton, and stretch/straighten tempo in tracks like Ableton ... ID BE REALLY REALLY HAPPY
Quote from: Triple Zero on January 28, 2012, 12:53:10 AM
srsly if you know any decent open source linux audio editor that can zoom and pan like Ableton, cut-n-paste clips like Ableton, design effect envelopes like Ableton, and stretch/straighten tempo in tracks like Ableton ...
Open source.... Grrr...... At a loss... In a pinch, beta test the linux version of bitwig studio for Linux immediately before it releases in march - bigwig dot com peeps defected from ableton and maybe we can convince them the only morally correct thing to do is release the stolen code as open source (just more subtle) oh and it runs 64 bit, whatever that matters - hope that works at least as a palliative....
Aha - qtractor qtractor.sourceforge.net (http://qtractor.sourceforge.net) - fits the bill :magick:
Posting in the thread for updates.
Trip I'm still working through the Google classes and combine it with a book on python 3.0 (i figure why not mix versions?).
The other night in #discord, ZGT informed me that MIT also offers python classes which can be found here ( i think this is the link anyway):
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-189-a-gentle-introduction-to-programming-using-python-january-iap-2011/
also, just checked out the 'regular expressions' google lesson incorporating the 're' module along with re.search() and re.findall()....
OMFG...what i could DO with this...
Quote from: Bu☆ns on January 28, 2012, 05:57:13 AM
also, just checked out the 'regular expressions' google lesson incorporating the 're' module along with re.search() and re.findall()....
OMFG...what i could DO with this...
Some people, upon being confronted with a problem, think "I'll use regular expressions!" Now they have two problems.
Quote from: Bu☆ns on January 28, 2012, 05:57:13 AM
also, just checked out the 'regular expressions' google lesson incorporating the 're' module along with re.search() and re.findall()....
OMFG...what i could DO with this...
don't forget re.sub
and YES, regexes are the fucking tits.
Alright so my main issue here is that I need to be able to create a standalone .exe file for my peers when I finally do create something worthwhile. I've installed a distutils extension, called py2exe, on all three of my python versions: 2.6, 2.7 and 3.0.
Doesn't work and I'm not exactly sure why.
Do any of you know of an alternative way to be able to call these python scripts on a machine that might not have python installed?
I have the portable versions of python also, but that really kicks the MB high on what might be a simple script. At that point I might as well just bust my way through using VBS or Powershell. If I end up having to use the portable python, how much of it can i strip away to reduce the overall file size? I assume I can remove unused modules...anything else?
Sorry, no idea about that. I never made stand-alone executables with Python. On Linux it's usually just there, or a package away.
I suggest to keep googling? Did you try searching on StackOverflow.com? They got good explanations of loads of stuff. Or you can post the question yourself.
Last time I checked (which was a while ago), py2exe didn't support all of Python.
When you say "it doesn't work" do you mean that you got an .exe that didn't do what the .py did, or that it failed to produce an .exe at all?
If the latter, looking for the error message py2exe gave you on StackOverflow (or just the general internet) is a good place to start.
If you're going with portable python, you can also strip out any .py file that has a corresponding .pyc file. (I think, haven't tried it personally. If that doesn't work, you almost certainly do it the other way around and remove the .pyc's.) You could also consider just installing python on your friends computers; if they're non-technical, they probably already have tons of random software lying around taking up space, and if they are somewhat technical, they need Python.
Another option:
See if your program works with http://www.skulpt.org/ . If it does, you can just have your friends run python through their web browsers.
Quote from: Golden Applesauce on February 04, 2012, 01:34:15 AM
Last time I checked (which was a while ago), py2exe didn't support all of Python.
When you say "it doesn't work" do you mean that you got an .exe that didn't do what the .py did, or that it failed to produce an .exe at all?
If the latter, looking for the error message py2exe gave you on StackOverflow (or just the general internet) is a good place to start.
If you're going with portable python, you can also strip out any .py file that has a corresponding .pyc file. (I think, haven't tried it personally. If that doesn't work, you almost certainly do it the other way around and remove the .pyc's.) You could also consider just installing python on your friends computers; if they're non-technical, they probably already have tons of random software lying around taking up space, and if they are somewhat technical, they need Python.
Another option:
See if your program works with http://www.skulpt.org/ . If it does, you can just have your friends run python through their web browsers.
Ah thanks!
I found a binary py2exe that seemed to install the missing dlls (MSVCR90.dll) appropriately. I tried it manually but I must have messed up along the way at some point...not sure, but it works now :D so that's enough for me at this point. I also found Pyinstaller (http://www.pyinstaller.org/) but i haven't tried it yet since py2exe seems to work. But www.skulpt.org will definitely come in handy, thanks!
Quote from: Triple Zero on January 26, 2012, 09:02:04 PMI believe there's several people on PD learning python (Faust, BDS), apparently Google's tutorials are pretty good:
http://blog.hartleybrody.com/2012/01/google-python/
http://sumukh.me/GFM5+ (PDF that's linked on the blog, in case it's down)
It was just brought to my attention that these Google tuts implicitly seem to imply that you're supposed to indent with two spaces in Python.
This is of course wrong, the Python Style Guide PEP 0008 (http://www.python.org/dev/peps/pep-0008/) is pretty clear that
four spaces is the convention.
Now this one is pretty arbitrary, but most of the recommendations in PEP 0008 are just plain common sense, and recommended if you want to write pretty Python code. One of the recommendations, for instance, is
consistency, which means that if you're adapting from someone else's code, and they use different style conventions, you should probably follow those, or it'll become a mess. And since
most of the Python community follows PEP 0008, it therefore follows that it's probably best to get in the habit of using four spaces yourself, just in case your code might some day touch someone else's code.
Another really good recommendation is that
A Foolish Consistency is the Hobgoblin of Little Minds, meaning that you should not be consistent when it's better that you don't. AKA Think For Yourself, Schmuck!
If you got any other questions about Python style, start your Python interpreter and type
import thisIf you don't have an interpreter handy, use this one: http://shell.appspot.com/ (it doesn't work on skulpt.org, it seems. also that appspot thing is overloaded and gives 500 errors often cause it's just a demo site)
Quote from: Triple Zero on January 28, 2012, 12:53:10 AM
Are you into generative art, by any chance?
Yea, even if just sequencing I like the stuff to somehow feel alive, surprises and such. I like building moving sound sculptures - and it also helps to sometimes have more complex automation when taking an orientation break during performance. It's a guilty pleasure of mine.
Not to push my vices, still, nodebox.net (http://nodebox.net) is cool like processing (above)
Oh, and yea, I think it's python...
Thanks for this Trip, I have to start using this image processing program that is Python based, so this is really useful.
Quote from: Triple Zero on January 27, 2012, 01:09:53 AM
If you're on Linux, I personally like to use gEdit, since it's really lightweight, but can be made to do all sorts of amazing things if you install the right plugins. Hard-core Linux coders seem to generally prefer Vim or Emacs, though. Vim's got a very steep learning curve because it operates rather differently than any text-editor you're probably used to, but there's a load of tutorials out there, especially since for some reason last year it got a surge in popularity. Emacs is easier to use right away, but it's huuuuuuuuge. Both Emacs and Vim can literally do anything.
Is there any plugins you'd suggest for gedit? I've been using it for a couple of months now, but I haven't looked at plugins.
Code comment -- Comment out or uncomment a selected block of code.
External Tools -- Execute external commands and shell scripts. You can do a *lot* of powerful stuff with this, if you're creative. For instance I added the following bit of script:
#!/usr/bin/python
print input()
With hotkey set to ctrl-shift-P, input to "current selection" and output to "replace selection", you can write *any* Python expression, select it, ctrl-shift-P and it gets evaluated and replaced. I mainly use it for small calculations such as 55+23+88+9 or whatever, but you can potentially go crazy with it.
Actually, turns out I don't even use that many coding specific ones.
Here's some general plugins that I've also enabled:
Document Statistics -- Analyzes the current document and reports the number of words, lines, characters and non-space characters in it.
Change Case -- Changes the case of the selected text.
Join/Split Lines -- exactly what it says on the label.
There's also a Python Console plugin, which gives you an interactive Python interpreter in a bottom pane. Very nice, but the default Python interactive interpreter is pretty basic. It works but it doesn't do completion, not much inspection--actually I just tried it and it's smoother than I expected, maybe it works for you.
There are also some third-party gedit plugins that do things like code completion and such, which is really nice, but I'm not sure why I am apparently not using those. Maybe they didn't work right, or got in the way. Or maybe I just figured I didn't need them because IPython's completion is superior anyway.
Indeed, if you're on Linux, I'd REALLY recommend getting IPython. It's the Python interactive interpreter souped up to a whole new level. Most of the code I write has this workflow:
- start IPython
- import some relevant libraries and modules or run -i thatscriptyouwereworkingon.py
- play around
- when you got something useful copy it to gedit (and save the script with .py extension for syntax highlighting)
- continue playing
- when needed run the .py script again with IPython's magic run command, preferably run -i because that way the script will be run in the interpreter's scope, and so you can access variables created by the script afterwards
- continue playing
- repeat until it's awesome
IPython does tab-completion, saves your past inputs *and* outputs in all sorts of handy arrays and variables. You get object inspection with name_of_the_object? and you get all sorts of useful info about its type and the doc string and where it was defined and such.
If you can also manage to get IPython's qtconsole running, you're in for even more treats. But that requires QT (which you probably have, it's a common GUI library, otherwise get it from the repo) and 0MQ aka ZMQ which you probably don't have and does not come as a binary package so you have to download its .tar.gz and build/install from source. If that sounds difficult, is okay, you'll only miss out on function argument tooltips, inline graphics and some other things, also slight buggyness because it's relatively new and experimental.
But really, DO get IPython.
Awesome, thanks! I'll check it out this arvo
(just posting this so I can clear the link from my open tabs :) )
Learn Python the Hard Way (http://learnpythonthehardway.org/book/intro.html) is a book that is free to read online as HTML (and can be bought in various ebook formats and probably dead tree as well).
I've heard many good things about it, even though I didn't really read it myself, since I'm not the target audience.
Part of the "thing" with the book is apparently that you're supposed to really *type out* the examples and exercises, not copy-paste them. Which I'm sure you'll agree definitely improves learning, though it might take a bit more effort.
Judging from the TOC it seems it will really take you for a full ride of all the fundamentals of Python. Including the bits that make it so damn awesome once you understand them, such as list comprehensions, generator expressions and decorators. Oh and of course classes and objects, I almost forget them cause they're so important I'd hesitate to call them "advanced" (fortunately they're not real difficult to grasp either, just pay real good attention when they get to the .__dict__ property, when you get that one, the rest is cake).
I'm also discovering that jEdit has a FUCKTON of plugins, and I'm betting I can tweak them until they make a really fine Python coding environment. But I will post about that later, when I have done so.
import antigravity
:lol:
(http://imgs.xkcd.com/comics/python.png)
http://sebsauvage.net/python/snyppets
I found that webpage useful for some random tips...also check out the http://sebsauvage.net/python (http://sebsauvage.net/python) main page for more.
I think I'm going to start teaching myself python. Maybe I'll eventually be able to get Blender 2.61 to import/export a model format I need instead of janking around with an older version of Blender importing an older version of the model format.
Or Just I dunno. I could be fun.
So I wrote a module to pull images from a tumblr blog and return their address to the bot on the IRC channel both randomly (command: !fy) and by tag query (command: !fy meryl streep).
Since I'm a total noob about Python, would anyone be willing to give it a critique?
I'm looking for tips on keeping it simple and 'pythonic' (w/e that means).
Is there a better way to do certain things?
Style and module suggestions?
from util import hook
from random import choice
import re
import urllib2
'''
This molule uses the urllib2 module to open the fuckyeahdementia tumblr blog and retrieve random images.
commands:
!fy -- this will go to the random page and extract the random jpg
!fy <input> -- this will search the blog's tags for images. If it finds one, it posts it, if doesn't find one it will return 'Not Found' or 'Not Found', 404
'''
@hook.command("fy") # hook command !fy
def fy(inp):
if not inp: # determines if there's a parameter or not if not, goes to the the random portion
urlsource = urllib2.urlopen('http://fuckyeahdementia.com/random') # Opens the page
s = urlsource.read() # reads the page
image = re.search('pi:pinit:media="(.+)" pi:pinit:layout="', s) # regex to extract the jpg address
imagegroup = image.group(1) # pulls out the jpg address from between the parenthesis, the (.+)
return "!!Random!! ~(nsfw risk)~ " +imagegroup
else:
urlsource = urllib2.urlopen('http://fuckyeahdementia.com/')
theurl = urlsource.geturl() # geturl to pull the url only
inp2 = inp.replace(" ", "-") # the parameter for website reguries spaces to convert to "-"
finalurl = (theurl) +"tagged/" +(inp2) # Completes the url for the prameter search
try:
urlsource = urllib2.urlopen(finalurl)
source = urlsource.read()
finalimage = re.findall('pi:pinit:media="(.+)" pi:pinit:layout="', source) # regex for finding the images and saving them to a list
if finalimage == []: # if finalimage list is empty return not found
return "Not Found"
else:
randimage = choice(finalimage) # randomly picks an item from the list and returns it
return "NSFW risk: " +randimage
except IOError, e:
if hasattr(e, 'code'):
return "Not Found", e.code
What is a Class? In plain terms, plz :)
In Object Oriented Programming, Classes can be thought of as abstract "templates" for Objects. It doesn't become an Object until you 'import' it into your current object-file, at which point the Class starts to setup default data/methods for the current Object. If another Class is imported after the first, it overwrites the previous default data.
So, for example, you could have a "Mammal" class, and then separate "Dog" & "Cat" classes, and then even a "Domesticated" class (which would overwrite some of the 'wild' data in the Dog and Cat classes), then you might have classes like "Farm Animal", "Hunting Companion" that could be applied to either Dogs or Cats and would override certain data in those archytypes.
Once it's all packaged up, it's an Object that you can query (i.e. is this Object a Mammal? Y/N Is this Object a Dog? Y/N etc, etc)
I hope that helps.
That's all correct for the concept of classes in Object Oriented Programming, and sort of the case for classes in Python as well.
If you really want to.
But in Python, you usually don't. (http://pyvideo.org/video/880/stop-writing-classes) (good video. recommend watching)
Instead, if classes must be used in Python, use them for what they simply are. Which is a function. A special kind of function.
>>> # see, this is a function
... def aap(x=3):
... print 'noot' * x
...
>>> # and if you call it, it works like this:
... aap()
nootnootnoot
>>> # and a function, like most things in Python is an object, which means you can do this:
... aap.mies = '774499'
>>> # what's that good for you ask? not much, it's just something you can do.
... print aap.mies
774499
>>> # also an object usually has a few other properties that it comes with. some are "special".
... # the "special" ones all start with __. for instance, a special property of nearly every object is __dict__:
... aap.__dict__
{'mies': '774499'}
>>> # neat huh? __dict__ shows you all properties in an object. except the ones that start with underscores
... # cause they're SECRET. a bit like hidden files, very useful to mess with, but kept out of sight.
... # another good one is __str__. that's a function that returns the string representation of the object.
... aap.__str__
<method-wrapper '__str__' of function object at 0x000000000909AF98>
>>> # yup that's a function (or method, same thing, nearly). let's call it!
... aap.__str__()
'<function aap at 0x000000000909AF98>'
>>> # that's the same thing you get if you'd write
... str(aap)
'<function aap at 0x000000000909AF98>'
>>> # okay now a class. I'm going to write it first then explain what happened.
>>> class Vrijdag(object):
... def __init__(self, hoeveel=2):
... print 'JEUMIG', hoeveel, 'DUIZEND VIS'
... self.vis = ['baars', 'schol', 'haai'] * hoeveel
... def vuur(self, wat='schol'):
... welk = self.vis.index(wat)
... self.vis[welk] = 'gebakken ' + self.vis[welk]
... def __call__(self):
... print 'HET ETEN IS KLAAR!!'
... print 'WE HEBBEN'
... for hap in self.vis:
... print hap
... def __str__(self):
... return ' EN '.join(self.vis)
... def __contains__(self, wat):
... return (wat in self.vis) or ('gebakken ' + wat in self.vis)
...
>>> maal = Vrijdag(3)
JEUMIG 3 DUIZEND VIS
>>> maal.vuur()
>>> maal.vuur('baars')
>>> print maal
gebakken baars EN gebakken schol EN haai EN baars EN schol EN haai EN baars EN schol EN haai
>>> maal.vuur()
>>> print maal
gebakken baars EN gebakken schol EN haai EN baars EN gebakken schol EN haai EN baars EN schol EN haai
>>> if 'schol' in maal:
... print 'JOEPIE SCHOL'
... maal()
...
JOEPIE SCHOL
HET ETEN IS KLAAR!!
WE HEBBEN
gebakken baars
gebakken schol
haai
baars
gebakken schol
haai
baars
schol
haai
First there's the class definition. If you're using Python 3.x you can leave out the "(object)" bit. That's just for Python 2.x to signify we're using "new style" classes. The old style classes are obsolete and you shouldn't bother with them unless you like having obscure bugs because they work ever so slightly different than the new classes.
Okay now as you can see, a class in Python is sort of a function, with functions in it. And all the functions in it always have the first parameter named "self" (you can actually pick any name for that parameter but everybody and their pet python write "self", so just do that). In many other languages this parameter is named "this", btw.
Funny thing is, if you call such a method, the "self" parameter is implicit. You don't have to give it, it'll just contain the object on whcih the method was called.
That's (sort of) the difference between functions and methods btw. Methods are functions that are also properties of some object. And usually they have an implicit "self" parameter that tells the method what object it's being called on. That way the object can modify and inspect itself.
But I'm getting ahead of things. First, we call the class:
>>> maal = Vrijdag(3)
JEUMIG 3 DUIZEND VIS
Now what actually happened is that the special named method __init__ got called, with a new empty object as the self parameter. Then the __init__ method does some things to self, such as putting some things in self.vis. Then (implicitly) the return value of __init__ is always self, which is stored in the variable maal.
So now we got an object of type Vrijdag in the variable maal.
Then we call a method on this object:
>>> maal.vuur()
Which, as you can see at vuur's definition, modifies an element of self.vis. Very nice! We do it again, but now with a particular vis named baars instead of the default schol:
>>> maal.vuur('baars')
And now we're getting to the magic parts! What happens if we try to print our maal? Python will try to convert the Vrijdag object to a string, which is done by calling the special method name __str__ on that object. Which we redefined! And it works, look:
>>> print maal
gebakken baars EN gebakken schol EN haai EN baars EN gebakken schol EN haai EN baars EN schol EN haai
Brilliant! But there's more special methods! For instance, remember how you can check if an element is in a list by asking if 'no pun' in ten_did? But what if ten_did is not a list but some other object? Well, Python does not give up easily and checks if said object has a special method named __contains__, calls it with the thing we're looking for and sees whether that method returns True or False. That's why this works:
>>> if 'schol' in maal:
... print 'JOEPIE SCHOL'
... maal()
And that last line? You thought maal was an object, not a function? Yeah so? What happens if you call it? Well glad you asked, Python goes looking for the special method __call__ and calls that instead!!
So now we're back full circle, and you can see that a function is basically just an object with a __call__ method.
Oh and we can also simply access the vis property of our maal:
>>> maal.vis
['gebakken baars',
'gebakken schol',
'haai',
'baars',
'gebakken schol',
'haai',
'baars',
'schol',
'haai']
Basically, in Python, a class is just a thing that keeps methods and properties together in objects. So you can bundle them up, which is useful. And you can make them perform all sorts of nifty tricks by using the special method names.
BTW here you can find a list of all these special method names: http://docs.python.org/reference/datamodel.html#special-method-names
There's zillions of em. You can redefine what happens if you add objects together, or compare them, or redefine any operator such as the % or ** or >>
Did that help?
Thanks, Tel and Trip!! I think I'm a lot better at understanding it today than I was yesterday. I think what I need to do now is go back and play around with classes and their special methods. I appreciate the info.
Another thing classes in Python are useful for, is again something that's quite different from your typical OOP language such as Java:
Have you learned about decorators yet?
A decorator is simply a function that takes a function as a parameter and returns another function (usually a transformed version of the parameter). Confusing? I hope not!
It's very simple, for instance, here we declare a function that takes a parameter func (which as you can see from its name is assumed to be a callable object, a function). Then it returns a new function that simply calls func twice:
>>> def twice(func):
... def new_func(*a, **kw):
... func(*a, **kw)
... func(*a, **kw)
... return new_func
There might be two weird bits about the above code. First: yes indeed we did just define a function inside another function and that's perfectly fine, don't look at me like that. Second, if the *a,**kw bit scares you, read the docs again (http://docs.python.org/tutorial/controlflow.html#keyword-arguments), but basically *a gets a list of any unnamed arguments and **kw gets a dict of any keyword arguments. It's done so it'll work with any sort of function, regardless what parameters it takes.
Let's see it in action!
>>> @twice
... def bloop():
... print "BLLOOOP!"
...
>>> bloop()
BLLOOOP!
BLLOOOP!
Works perfectly! You put a decorator above a function definition, with a @ in front of it, and then it'll "decorate" that function. So, by decorating the function bloop with the @twice decorator, bloop is fed to the twice function and replaced by its return value, being a function that calls bloop twice.
So how does it know to call the old bloop twice and not the newly decorated bloop? (which would result in an infinite loop of bloop, or infinite bloop, perhaps).
Well, that's another cool thing you get with defining functions inside of other functions: closures
When Python sees the definition of bloop with the decorator above it, it calls the function twice with bloop as a parameter. Inside this function, the parameter looks like a local variable named func right?
And then inside the function, we define another function named new_func. However, this new_func code contains a reference to the variable func (two, even). BUT!! The variable func is not local to new_func, it's from one level higher, it's local to twice. And we all know what happens to local variables when you exit their scope, right? They vanish! We can't have that because after we exited twice, we still got new_func which might still need to use that variable!
Fortunately, Python is very smart, recognizes that there is a local variable, used inside an inner function, and it creates a "closure".
Basically what that means is that it creates a frozen copy of the local variables referenced by new_func and attaches them to new_func to keep. And that frozen copy is our old bloop, and not the new one.
See a closure is kind of a bit like that science fiction bullshit when they say you can cut off a little bubble of space-time and create a tiny parallel universe, except without the hand-wavy stuff because Python always lets you look at its dirty insides:
>>> bloop()
BLLOOOP!
BLLOOOP!
>>> bloop.func_closure
(<cell at 0x000000000844DAC8: function object at 0x00000000082E9438>,)
>>> bloop.func_closure[0]
<cell at 0x000000000844DAC8: function object at 0x00000000082E9438>
>>> bloop.func_closure[0].cell_contents
<function bloop at 0x00000000082E9438>
There! You see it? There it is!!HA! That's our old bloop! Check it, we can call it and:
>>> bloop.func_closure[0].cell_contents()
BLLOOOP!
Just once! Whooo
(honest I never actually tried this before, but shit just works)
Next post will give another way of making a decorator!
There is another way besides making a function in a function! Remember how a class works?
To make an object instance from a class, you basically "call" it, right?
Actually that's exactly what happens, not just "basically". SO if the __init__ method of the class accepts a function as the second parameter (because the first one is always the implicit "self") ... then ... are you feeling it?
... then the __init__ function returns the new instance and the decorated function is replaced with that instance object.
so ... when people might try to call this decorated function, that means the object must know what to do, for which we can use the special method __call__ !!
>>> class twice(object):
... def __init__(self, func):
... self.func = func
... def __call__(self, *a, **kw):
... self.func(*a, **kw)
... self.func(*a, **kw)
See? It can be used exactly the same as the previous decorator:
>>> @twice
... def bloop():
... print "BLLOOOP!"
...
>>> bloop()
BLLOOOP!
BLLOOOP!
Except that it doesn't use a closure, but it saves the old version inside the object instance itself, as a property called "func":
>>> bloop
<__main__.twice object at 0x000000000844EB70>
>>> bloop.func
<function bloop at 0x0000000008EC80B8>
>>> bloop.func()
BLLOOOP!
Next post: doing something USEFUL with a decorator!
Ok say you made a very cool class for playing Rock Paper Scissors, like this one:
>>> class Roshambo(object):
... options = ['ROCK','PAPER','SCISSORS']
... def __init__(self, what=None):
... if what not in Roshambo.options: # if we don't supply a choice, pick a random one
... what = random.choice(Roshambo.options)
... self.what = what
... def __str__(self):
... return self.what # just useful for printing
... def __eq__(self, other):
... return self.what == other.what
... def __gt__(self, other):
... if self.what == 'ROCK' and other.what == 'SCISSORS':
... return True
... if self.what == 'PAPER' and other.what == 'ROCK':
... return True
... if self.what == 'SCISSORS' and other.what == 'PAPER':
... return True
... return False
See it's got a special method __str__ so you can print it and see what's inside. And it also got a method __eq__ (equals) so you can compare two players and see if they tie, and finally __gt__ (greater than) so you can see if one player wins over the other. Like this:
>>> trip = Roshambo()
>>> burns = Roshambo()
>>> if trip > burns:
... print "Trip wins!"
... elif trip == burns:
... print "It's a tie!"
... else:
... print "Burns wins!"
...
It's a tie!
>>> print trip, burns
PAPER PAPER
Seems to work, right?
But what if you'd also want to use the < operator? And maybe even the <= and the >= and the != operators? Well you could write a special method for all of those, but that would get really boring. And besides, we already defined == and >, so from there Python could figure out how the other operators work, right? Because A<B means the same as "not A>B and not A==B" and so it goes.
Turns out, Python is smart like that! And you can import a special decorator from the functools module.
Oh btw did I mention that you can decorate classes in exactly the same way as you can do functions? Well, you can.
like this:
>>> import functools
>>> @functools.total_ordering
... class Roshambo(object):
... options = ['ROCK','PAPER','SCISSORS']
... # ... etc rest of the class is identical
... # ...
>>> trip = Roshambo()
>>> burns = Roshambo()
>>> if burns < trip:
... print "Trip wins!"
... elif trip == burns:
... print "It's a tie!"
... else:
... print "Burns wins!"
...
Trip wins!
>>> trip >= burns
True
See? I WON!!!
I mean, also what's nice is that we can use all of the comparison operators, by just defining two of them, and the @functools.total_ordering decorator will figure out the rest!
Well. And that is nice.
Thanks, Trip...
So now I'm trying to code something that will create an .m3u playlist file from the contents of a directory.
What I want it to do is to request two user inputs:
1. It requests a folder path to the folder containing mp3s (to be later updated to include a 'browse gui' once I'm ready to open up the gui can of worms)
2. I want it to request a playlist name, or, if just left blank to grab the folder basename and concatenate that to '.m3u'.
Now I have a working script that will do exactly that:
import os
import glob
directory = raw_input("Enter file folder path: ")
os.chdir(directory)
playlist_title = raw_input("Enter playlist title (optional): ")
if not playlist_title:
newtitle=os.path.basename(directory)
for files in glob.glob("*.mp3"):
f = open(newtitle+".m3u", "a")
f.writelines(files+"\n")
f.close()
else:
for files in glob.glob("*.mp3"):
f = open(playlist_title+".m3u", "a")
f.writelines(files+"\n")
f.close()
But since I'm trying to understand OOP and classes I've been trying to re-write it in that form.
So far this is what I have:
class playlist:
def change_directory(self, filepathlocation):
'''moves script to working dir'''
os.chdir(filepathlocation.replace('"', ''))
def write_the_file(self,title):
'''writes the .m3u file'''
for files in glob.glob("*.mp3"):
f = open(title + ".m3u", "a")
f.writelines(files+"\n")
f.close()
def playlist_title(self, title):
'''creates the m3u playlist title either from user input or directory basename'''
if title == None:
title = os.path.basename(playlist.change_directory(filepathlocation))
playlist.write_the_file(title)
else:
playlist.write_the_file(title)
playlist = playlist()
filepathlocation = playlist.change_directory(raw_input("Enter file folder path "))
title = playlist.playlist_title(raw_input("Enter playlist title (optional): "))
Now, where I'm stuck is in the playlist_title() function. When I just leave the playlist title empty it does complete but it doesn't concatenate the input variable 'title' to the '.m3u' extension. IOW, I get an .m3u file titled '.m3u'.
Questions:
1. In flow control, what is the difference between if title == None: and IF NOT title: ? Both appear to be the same thing and seem to work the same depending on how I write the code. Is it better, or perhaps more 'pythonic' to write one over the other?
2. Why does os.path.basename(directory) work in my original, process oriented script but not in my object oriented script?
Thanks, folks ;)
ETA: I realize that the original form of the script is probably a better way to accomplish this task (especially in regard to the video earlier ITT), but since I'm still trying to wrap my head around classes and oop, I figured the script was easy enough to duplicate.
First, you probably don't want to read from standard input with raw_input, instead you want to make a commandline tool and use the argparse module (see python docs).
The rest of your code I'll have a look at later.
About making GUI things, I don't have a lot of experience with that but there's probably some good tutorials out there.
QT is a good GUI library I've heard good things about. It also has Python-bindings, afaik. These articles use C++, but might still be some help (or not) : http://www.tuxradar.com/learnqt
i got it... Enki-][ helped a bit in IRC.
The current working directory parameter was outside the scope of the playlist_title function. (I think I said that correctly).
That's interesting how that all works out. I can see I definitely need to become more aware of this.
import os,glob
class playlist:
def change_directory(self, filepathlocation):
'''moves script to working dir'''
os.chdir(filepathlocation.replace('"', ''))
def write_the_file(self,title):
'''writes the .m3u file'''
for files in glob.glob("*.mp3"):
f = open(title + ".m3u", "a")
f.writelines(files+"\n")
f.close()
def playlist_title(self, title):
'''creates the m3u playlist title either from user input or directory basename'''
if not title:
cwd = os.getcwd()
title = os.path.basename(cwd)
playlist.write_the_file(title)
else:
playlist.write_the_file(title)
playlist = playlist()
filepathlocation = playlist.change_directory(raw_input("Enter file folder path "))
title = playlist.playlist_title(raw_input("Enter playlist title (optional): "))
I wonder if this might be useful to any of you guys:
www.ironspread.com -- Script Excel with Python (http://www.ironspread.com/) (and here's the HN THread (http://news.ycombinator.com/item?id=4085052))
TRIP! THANKS! I was looking for something like this!! I kept putting off learning more VBA because, well frankly it's so clunky. I have some comma separated values and other data I've been needing to organize with Excel but didn't have the time to go diving into VBA. :mittens:
:thanks::1fap:
Quote from: Bu☆ns on May 20, 2012, 06:09:57 PM
Questions:
1. In flow control, what is the difference between if title == None: and IF NOT title: ? Both appear to be the same thing and seem to work the same depending on how I write the code. Is it better, or perhaps more 'pythonic' to write one over the other?
"title == None" checks for exactly what you'd expect - that the value of title is None. "if not title" will check if title is any 'falsey' value, of which there are many: None, False, 0, "", [], and maybe some others. (I think you can give your own classes truthiness values?)
In general, you probably want the explicit test whenever possible - it's easy to make code errors by accidentally testing for something that you didn't mean to. For example, you might have a TryParseToInt(string) function that converts a string to an integer, or None if the string didn't represent an integer (to avoid throwing an exception.) Code like:
num = TryParseToInt(user_input)
if num:
# valid input case - do stuff
else:
# re-ask for input
will
not do what you expect in the event the user enters "0" - which is a value that your code wants to treat as valid but the if statement treats as false.
Quote from: Golden Applesauce on June 11, 2012, 02:24:32 PM
Quote from: Bu☆ns on May 20, 2012, 06:09:57 PM
Questions:
1. In flow control, what is the difference between if title == None: and IF NOT title: ? Both appear to be the same thing and seem to work the same depending on how I write the code. Is it better, or perhaps more 'pythonic' to write one over the other?
"title == None" checks for exactly what you'd expect - that the value of title is None. "if not title" will check if title is any 'falsey' value, of which there are many: None, False, 0, "", [], and maybe some others. (I think you can give your own classes truthiness values?)
In general, you probably want the explicit test whenever possible - it's easy to make code errors by accidentally testing for something that you didn't mean to. For example, you might have a TryParseToInt(string) function that converts a string to an integer, or None if the string didn't represent an integer (to avoid throwing an exception.) Code like:
num = TryParseToInt(user_input)
if num:
# valid input case - do stuff
else:
# re-ask for input
will not do what you expect in the event the user enters "0" - which is a value that your code wants to treat as valid but the if statement treats as false.
Thanks for that detail :)
It makes sense that if the code
needs to be specific then it
should be specific.
Quote from: Bu☆ns on June 11, 2012, 09:29:42 PM
Thanks for that detail :)
It makes sense that if the code needs to be specific then it should be specific.
Looking back at your code, this was actually one of your problems in the non-working version.
Quote from: Bu☆ns on May 20, 2012, 06:09:57 PM
class playlist:
def change_directory(self, filepathlocation):
'''moves script to working dir'''
os.chdir(filepathlocation.replace('"', ''))
def write_the_file(self,title):
'''writes the .m3u file'''
for files in glob.glob("*.mp3"):
f = open(title + ".m3u", "a")
f.writelines(files+"\n")
f.close()
def playlist_title(self, title):
'''creates the m3u playlist title either from user input or directory basename'''
if title == None: # not what you meant!
title = os.path.basename(playlist.change_directory(filepathlocation))
playlist.write_the_file(title)
else:
playlist.write_the_file(title)
playlist = playlist()
filepathlocation = playlist.change_directory(raw_input("Enter file folder path "))
title = playlist.playlist_title(raw_input("Enter playlist title (optional): "))
Now, where I'm stuck is in the playlist_title() function. When I just leave the playlist title empty it does complete but it doesn't concatenate the input variable 'title' to the '.m3u' extension. IOW, I get an .m3u file titled '.m3u'.
raw_input() always returns a string of some kind - if the user doesn't actually type any characters, it just returns the empty string "". Since "" != None, you were always hitting the else: case and calling write_the_file() with an empty string. So your code never actually tried to evaluate filepathlocation - if it had, it would have bugged out and given you "NameError: name 'filepathlocation' is not defined" instead of completing correctly. (Since it wasn't in scope, as you learned from Enki.) And if filepathlocation
were in scope, you would have run into another problem which is that change_directory
doesn't return a value returns None (as it should), so you'd have been calling os.path.basename() on None, which throws a exception. (Note that in your current program, filepathlocation, which is assigned to but never used, has a value of None after the program executes.)
There's a really subtle bug in your current working program - try changing the variable named "playlist" to something else, like "the_playlist".
playlist = playlist()
filepathlocation = playlist.change_directory(raw_input("Enter file folder path "))
title = playlist.playlist_title(raw_input("Enter playlist title (optional): "))
to
the_playlist = playlist()
filepathlocation = the_playlist.change_directory(raw_input("Enter file folder path "))
title = the_playlist.playlist_title(raw_input("Enter playlist title (optional): "))
The resulting error, the fix, and how your code managed to work anyway, will blow your mind. :)
On object-oriented programming in general -
It's not easy to "get" OOP until you've had to maintain bad OOP code and had the pleasure of working on good OOP code. The defining trait of bad code isn't that it fails to work correctly, but that modifying it safely is a pain in the ass that forces future developers working on your project to make compromises. So code that is only really going to be run a handful of times and only ever in one environment - like your playlist making script - never needs to be updated, so putting in the effort to do OOP correctly feels like wasted effort because you're not around to see the benefits.
If you're interested in really learning how to do object orient programming correctly, I'd recommend looking into SOLID programming (http://en.wikipedia.org/wiki/Solid_(object-oriented_design)), the DRY principle (http://en.wikipedia.org/wiki/DRY), and related terms (http://en.wikipedia.org/wiki/List_of_software_development_philosophies). Uncle Bob's Clean Code (http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/) is also a great book.
With all that said - your current script really doesn't take any advantage of being object oriented. The whole program would be vastly simplified if you dropped the class declaration and moved all the method declarations to global functions; if nothing else, that saves you from having to instantiate a playlist() object. That we can do that is our first code smell - playlist() encapsulates no state. This isn't necessarily a bad thing, but in our case it is, because the program is keeping track of some state: the current working directory. It's just doing so by setting the CWD for the entire program, which is wrong because only playlist cares about the CWD, so only the playlist should know about / have the opportunity to muck with the CWD. If some other component of the program also cared about the CWD, it would have to dance around playlist to avoid messing it up, which is a lot of extra effort and opportunities to create bugs.
So the first refactoring I'd do would be to change playlist.change_directory to something like this, with the corresponding change in write_the_file:
def change_directory(self, current_directory):
self.current_directory = current_directory
...
def write_the_file(self, title):
...
f = open(self.current_directory + title + ".m3u", "a")
...
Note that I left off the logic that deals with stripping the quotes off of the user input. That's related to the Single Responsibility Principle (http://en.wikipedia.org/wiki/Single_responsibility_principle), which basically says that each class should do one and only one thing. Worrying about how to parse user input is someone else's job - we'll just say that it's the responsibility of whoever calls change_directory make sure that they provide a valid current_directory parameter.
That brings us to another responsibility that playlist has taken upon itself, which is that it finds all of the .mp3 files in addition to creating a playlist based on them. This means that if we wanted to change the way we get music filenames (maybe you reorganize your collection and now want to get relative paths to individual .mp3s inside a music/artist/album/song.mp3 folder structure, or you get some .ogg's, or you want do the same thing for videos, or you want the songs sorted in a particular way) we have to open up playlist's source and start tweaking things. This is where OOP starts to shine - we can create a separate object that's responsible for getting which files we want, and then have it tell playlist what files to include in the playlist. If we have more than one way in which we want to get the filenames, we just make new implementations of GetFilenames or whatever and use them with playlist - currently, we have to make an entirely new playlist class and duplicate all the other playlist logic if we want to get different files.
There are two basic ways to go about this. The first is to simply extract glob.glob("*.mp3") to a parameter, so you end up with something like:
def write_the_file(self, title, file_names):
f = open(self.current_directory + title + ".m3u", "a")
for file_name in file_names:
f.write(file_name) # let file.write worry about line endings for you
f.close()
where you just run glob.glob when you call write_the_files. The advantage to this is that if we wanted to add multiple groups of files to a single playlist, we can just call write_the_file with a different list of file_names and they'll all get appended to the same playlist file. The disadvantage of this is that there's a good chance our file-finding function is going to depend on CWD (like glob.glob does), which would require us to duplicate code - we have to tell playlist and our file-finding source what CWD is separately. We can fix that by passing either a function or an object exposing a specifically-named method to write_files instead of just the list:
def write_the_file(self, title, GetFilesFromCWD)
...
for file_name in GetFilesFromCWD(self.current_directory):
...
the_playlist.write_the_file("cool tunes", lambda cwd: glob.glob(cwd + "*.mp3"))
The only potential problem here is that if we really do want to get our music files in the same way most of the time, we end up repeating our file-getter expression every time we need to call write_the_file. That's not very DRY, so in that use case I'd recommend using composition: each playlist object will simply contain another object that knows how to get a list of files to be added to the playlist.
class playlist:
def __init__(self, file_name_getter):
self.file_name_getter = file_name_getter
...
def write_the_file(self, title):
...
for file_name in self.file_name_getter.get_files(self.current_directory)
...
This is perfect for the case where the main use of a playlist object is going to be to create a number of different playlists, each in a different working directory and/or with a different title, but each time you want to the find music files in the same way relative to the current directory. If the main use of a playlist would be to sit in one directory and append different groups of songs to the same playlist, I'd go with the earlier approach of just passing either the list of file names or a function to generate them directly, and put title on the __init__ so I didn't have to pass the same title over and over. Then we could drop playlist_title and either move its title-fixing logic into the constructor or just require the caller to pass a valid title. In fact, in that case I'd require users of playlist to pass a valid directory into the constructor and do away with the change_directory method; if every instance of playlist needs it's directory to be set exactly once, then I don't want to allow people to forget to do it and there's no need to go to extra effort to allow people to set it more than once. That would be a more subtle case of violating DRY - requiring both foo = playlist() and foo.change_directory() is like making someone order their water and a glass to hold it separately; the former implies the latter.
http://pyvideo.org/ ooooooooo