=== James is now known as Guest29919 === SergioMeneses_ is now known as SergioMeneses === Mrokii_ is now known as Mrokii === ZachK_ is now known as zkriesse === Eric is now known as Guest36399 === zumbi_ is now known as zumbi === Mohan_chml is now known as mohi1 === kdrx-[-_-]\ is now known as kuadrosx === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - https://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Questions in #ubuntu-classroom-chat || Event: Beginners Team Dev Academy - Current Session: Introduction to Python: Part 3 - Instructors: pedro3005 [18:01] Why, hello everyone [18:01] er.. is anybody out there? [18:01] *tumbleweed rolls by* [18:03] Alright, let's go on [18:03] * pedro3005 checks logs [18:03] Well, last time we were talking about lists [18:03] a quick refresher to you guys [18:04] numbers = [1, 2, 3, 4, 5] [18:04] so we have various methods within lists to help us deal with them, as was explained [18:04] http://docs.python.org/tutorial/datastructures.html#more-on-lists you can see them here if you forgot [18:05] (i know you did, don't lie) [18:05] Well, anyway, today I would like to talk a bit more on list slicing [18:05] We can get a specific element or number of elements from lists using that [18:05] take the numbers list for example [18:05] numbers[0] is 1 [18:06] we always start counting at 0 [18:06] numbers[1] is 2 and so forth [18:06] we can use the colon to indicate more than 1 element [18:06] say I want everything from the beginning to the 3rd element? [18:07] numbers[0:3] will yield that [18:07] a list with [1, 2, 3] [18:08] I can use -1 to go back [18:08] numbers[-1] is the first element of the list counting from right to left [18:09] and I can also define the step with an extra colon [18:09] for instance [18:09] numbers[0:5:2] returns [1, 3, 5] [18:09] because it grabs every other number from position 0 to element 5 [18:10] All of this list slicing also works with strings [18:10] >>> "abcd"[0] [18:10] 'a' [18:11] Mohan_chml asked: Is there any difference in using for loop and slicing OR it is just code efficiency? [18:11] it makes code smaller and neater [18:11] and easier to understand too [18:12] For dealing with strings, we have this method called split() [18:12] it splits a string (returning a list of strings) by a certain character [18:12] for instance, space [18:13] "a b c d".split(" ") will return ['a', 'b', 'c', 'd'] [18:13] split() is the same as split(" ") [18:13] you can also split by comma, for instance [18:13] "a,b,c,d".split(",") will do the same [18:14] the inverse of the split function is join [18:15] Otto56 asked: Does split accept multiple seperator characters or does it then split on teh sequence / sub-string? [18:15] No, your example would split by ":;" [18:15] eg split(";:") [18:15] err, I mean ";:" [18:16] You can use regular expressions for what you want [18:16] (but I don't know regexp :P) [18:16] or you can call subsequent split methods [18:16] wait.. no you can't [18:16] never mind that [18:16] As I was saying, the join function [18:17] you call it with a string and a list and it will join each element of this list with a string between them [18:18] let me exemplify [18:18] ", ".join(['a', 'b', 'c', 'd']) [18:19] that's pretty much it about join [18:19] any questions? [18:20] there are a whole bunch of string methods, and it's not my goal to go over each of them [18:20] many are self-explanatory [18:20] you have for instance str.startswith() and str.endswith() [18:20] check the python docs for all [18:20] http://docs.python.org/library/stdtypes.html#string-methods [18:20] I would like to introduce a new concept here [18:21] before we go on to more data structures, like dicts etc. it's important to learn functions [18:22] All this time, we've been using various functions [18:22] raw_input(), split(), they're all functions [18:22] and we call functions sometimes with parameters, like split(" ") [18:22] that's calling split with " " as argument [18:23] We can define our own functions [18:23] This is useful for a procedure that you're doing various times in your code [18:23] to avoid writing the same thing over and over, you define a function [18:23] for instance, let's define a function that prints a help message [18:24] def print_help(): [18:24] print "You're on your own" [18:24] now that that's done, when you call print_help() it'll display that message [18:25] you can add more print commands to it [18:25] We also learned about boolean expressions, and we can make functions that answer boolean questions [18:26] for instance, let's create a function that checks if a number is negative [18:26] def is_negative(number): [18:26] if number < 0: [18:26] return True [18:26] return False [18:27] let's analyze this [18:27] the function is_negative accepts a parameter, called number [18:27] if this number is smaller than 0, it returns True [18:28] the thing is, when a function returns a value, it immediately stops executing and goes back to the main code [18:28] so it wouldn't hit return False [18:28] but if the if block was not run, that is, number > 0, it would just read return False [18:28] ;) [18:28] you could otherwise rewrite that function as [18:28] def is_negative(number): [18:28] if number < 0: [18:28] return True [18:28] else: [18:28] return False [18:28] but it's one line too big :) [18:29] we can write functions that accept more than one parameters [18:29] for instance, let's make a function that multiplies two values [18:30] def mul(x, y): [18:30] return x * y [18:30] but what if I wanted a function to double a number? [18:30] I could try writing it as this (it is _WRONG_) [18:30] def double(x): [18:31] x = 2 * x [18:31] why is this wrong? [18:32] because the variable we're dealing with inside the function is a copy of the variable you called the function with [18:35] if we only alter the copy, it won't alter the original variable [18:35] Otto56 asked: Is it possible to pass values in by reference? [18:35] like in C? no [18:35] we can make global variables [18:35] can I brb for some minutes? family calls [18:36] sorry everyone [18:44] back [18:44] For this problem, we have two solutions [18:45] if we're dealing with a specific variable, we can make it global [18:45] a = 2 [18:45] def double_a(): [18:45] global a [18:45] a = a * 2 [18:46] Or, we can return the new value and set the old variable equal to that [18:46] def double(x): [18:46] return 2 * x [18:46] a = 4 [18:46] a = double(a) [18:46] that would yield 8 [18:47] a function can return and accept any object [18:47] and everything is an object [18:48] you can for instance define a function that completes a sentence with the period, for instance [18:49] err, too many for instance's [18:49] but let's show that [18:49] def complete_sentence(sentence): [18:49] if sentence.endswith("."): [18:49] return sentence [18:50] return sentence + "." [18:50] you could modify that function to check that the first letter is a capital letter etc [18:50] (left as an exercise for the reader :P) [18:51] a function can even accept another function as argument (do I hear some functional programming?) [18:52] for instance, imagine we have functions that return some numeric value. e.x. minus_one(x), minus_two(x) ad infinitum. (what they do is fairly obvious, and so is the implementation) [18:53] we want to make a generic function which calls it a number is negative and record the number of steps (for some freakish reason. my examples suck. sorry) [18:53] give me a minute to write that === harrisonk_away is now known as harrisonk [18:55] >>> def call(f, x): [18:55] ... if x < 0: [18:55] ... return 0 [18:55] ... steps = 0 [18:55] ... while x: [18:55] ... x = f(x) [18:55] ... steps += 1 [18:55] ... return steps [18:55] ... [18:55] let's analyze that step by step [18:55] (day by day...) [18:55] sorry, let's continue [18:55] Well, if x is already smaller than 0 (negative), it took 0 steps, right? [18:55] so we return that [18:56] we continue the function (if it's positive) by initializing the number of steps at 0 [18:56] while x, that is, while x > 0 [18:56] hmmm [18:56] maybe I should change that to while x >= 0 [18:56] yes [18:57] >>> def call(f, x): [18:57] ... if x < 0: [18:57] ... return 0 [18:57] ... steps = 0 [18:57] ... while x >= 0: [18:57] ... x = f(x) [18:57] ... steps += 1 [18:57] ... return steps [18:57] ... [18:58] that is correct now [18:58] Same thing [18:58] while x is bigger or equal to 0, that is, non-negative [18:58] we run these steps [18:58] first, we set x to be f(x), that is, we call the function upon x [18:58] we raise the step count by 1 and continue iterating [18:58] when it is done, we return steps [18:59] so, consider we had the function minus_one() and the value 4 [18:59] call(minus_one, 4) [18:59] returns 5, as expected [18:59] we type minus_one like that, without parenthesis () [18:59] because we're not calling the function, we're passing it as argument [19:00] python has the built-in function map() but we can rewrite it for fun [19:00] one min [19:03] >>> def new_map(f, values): [19:03] ... final = [] [19:03] ... for x in values: [19:03] ... final.append(f(x)) [19:03] ... return final [19:03] ... [19:04] so, what does this do? [19:04] it accepts a list and calls the function f on each element of the list, returning a new list with the returned values [19:04] >>> new_map(minus_one, [1, 2, 3]) [19:04] [0, 1, 2] [19:04] (again, python already has the function map()) [19:05] Otto56 asked: What is the python terminology for this? I understand it as a function pointer or delegate [19:05] hm.. I'm not aware of a specific terminology [19:05] it's a function [19:05] it's not a pointer to a function, it IS a function [19:05] forget pointers :P [19:06] (how I hate them) [19:06] anyway [19:06] We could write the map() function more easily using list comprehension, but I didn't go over that [19:07] any questions? does everyone understand the functions? === harrisonk is now known as harrsonk_away [19:17] So let's go back to lists with list comprehension [19:17] this is useful, I promise [19:18] list comprehension is a method of constructing a new list by some iteration [19:18] it's useful for instance to unpack strings [19:19] let's say I want to get a string "abcd" and turn it into a list with each character, i.e. ['a', 'b', 'c', 'd'] [19:19] newlist = [x for x in "abcd"] [19:20] but I can do things with this first x [19:20] let's say I want to ensure that all characters are lower-case [19:20] we have the function lower() [19:20] newlist = [x.lower() for x in "AbcD"] [19:21] or, imagine this [19:21] we have a list of lists [19:21] [[1, 2], [3, 4], [5, 6]] [19:22] we want a simple list with all these values multiplied, as in, 1 * 2, 3 * 4, 5 * 6 [19:23] we can do that with: [19:23] newlist = [x * y for x, y in oldlist] [19:23] (given oldlist = [[1, 2], [3, 4], [5, 6]]) [19:24] we're unpacking each element of the list (another list) [19:24] let me exemplify a simpler case [19:24] imagine we have a = [1, 2] [19:24] we can do [19:24] x, y = a [19:24] we are unpacking a [19:24] x = 1, y = 2 [19:25] so, as I mentioned, we can rewrite the map function using list comprehensions [19:25] def new_map(f, x): [19:26] return [f(x) for val in x] [19:26] errrrr [19:26] def new_map(f, values): [19:26] return [f(x) for x in values] [19:27] we can use this to improvise multiple-argument maps [19:27] for instance, the built-in function max() returns the max value of the list it is called with [19:28] hmm, no , forget that [19:28] hehe [19:28] someone give me a function with two arguments :P [19:29] ohh [19:29] we have the cmp() function. it accepts two integers [19:29] it returns negative if x < y, zero if x == y, and positive if x > y (given cmp(x, y)) [19:29] we can map that [19:30] a = [[1, 2], [3, 3], [4, 3]] [19:30] vals = [cmp(x, y) for x, y in a] [19:30] >>> vals [19:30] [-1, 0, 1] [19:31] questions? [19:32] we can add boolean expressions to list comprehensions [19:32] say I want all positive values from a list [19:32] a = [1, 2, -5, 0, 4] [19:33] newlist = [x for x in a if x > 0] [19:34] we can use all sorts of expressions there [19:34] for instance, we can call int() but only if it's a digit [19:35] >>> a = ['a', "2", "c", "4"] [19:35] >>> [int(x) for x in a if x.isdigit()] [19:35] [2, 4] [19:35] we can use this to strip out of a string all upper-case characters for instance [19:36] message = "HELLO there MY friend" [19:36] >>> [x for x in message.split() if not x.isupper()] [19:36] ['there', 'friend'] [19:37] this one is a bit more complex so I'll go over it [19:37] I wanted to separate message by each word it had, so I called message.split(). if you recall, that defaults to split(" "), that is, grab by each space [19:37] so we're effectively getting each word [19:38] if not x.isupper() would be equal to saying if x.isupper() == False [19:38] but shorter and in my opinion clearer [19:39] because it's closer to natural language [19:39] Questions? [19:42] Alright [19:42] Now, as I promised last class, I will solve a couple problems [19:42] first I must introduce a simple built-in function, range() [19:42] it does arithmetic progressions [19:43] >>> range(10) [19:43] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [19:43] range(10) means that stop = 10. it stops at 10 without including 10. And it defaults to starting at 0, but you can specify [19:43] >>> range(1, 10) [19:43] [1, 2, 3, 4, 5, 6, 7, 8, 9] [19:43] now we started at 1 [19:44] we can also specify the step [19:44] >>> range(1, 10, 2) [19:44] [1, 3, 5, 7, 9] [19:44] picking every other number [19:44] the step may be negative to count descending [19:44] >>> range(10, 0, -1) [19:44] [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] [19:45] What is this useful for, you ask [19:45] well, many things [19:45] let's take for instance this problem [19:45] Add all the natural numbers below one thousand that are multiples of 3 or 5. [19:46] well, we'll be working with the range of 1 - 1000 (we can skip 0, since it's not a multiple) [19:46] let's ask ourselves, how can we translate "multiple of 3 or 5" to python? [19:47] well, if x is a multiple of 3, when we divide x by 3 we get 0 rest [19:47] right? [19:47] so we use the % operator [19:47] meaning that if x is a multiple of 3, x % 3 == 0 [19:47] now it's easy! [19:47] first, we get a variable to store the sum [19:47] value = 0 [19:48] for num in range(1, 1000): [19:48] if num % 3 == 0 or num % 5 == 0: [19:48] value += num [19:49] after the loop is done, value will hold the sum of all the natural numbers below one thousand that are multiples of 3 or 5 [19:50] Questions? [19:58] The old classic FizzBuzz (let's see if I have enough time) [19:59] we must loop from 1 to 100, print Fizz for multiples of 3, Buzz for multiples of 5, and FizzBuzz if both [19:59] for num in range(1, 101): # gotta account for 100 [20:00] if num % 3 == 0: [20:00] err, wait [20:00] answer = "" === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - https://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Questions in #ubuntu-classroom-chat || [20:00] lol damn [20:01] ok [20:01] FizzBuzz is left as homework! [20:01] :P [20:01] I already gave a hint [20:01] ;) === pedro_ is now known as pedro3005 [20:41] For those interested, i started a home page for pedro3005's Intro to Python class: https://wiki.ubuntu.com/BeginnersTeam/FocusGroups/Development/Academy/IntroToPython [22:02] pedro3005: question: in the above class you referred several times to f(), and you passed it in as an argument as: somefunc(f,x), but it was never defined. is that a function defined by python or one that you would define (but you just didn't in these examples)? [22:02] nUboon2Age, double(x): return x + x [22:02] is x defined anywhere else in the code? [22:04] sorry i'm still not understanding pedro3005 [22:05] nUboon2Age, [22:05] >>> def double(x): [22:05] ... return x + x [22:05] ... [22:05] >>> double(2) [22:05] 4 [22:06] x is not defined elsewhere, just in that function [22:06] the variable x exists only in the double function, when it is called. x is a copy of whatever value was passed to it [22:06] okay [22:06] makes sense so far [22:06] similarly, we can do this with functions [22:07] for instance, a function that calls f on x (for example purposes) [22:08] >>> def call(f, x): [22:08] ... f(x) [22:08] ... [22:08] >>> def p(x): [22:08] ... print x [22:08] ... [22:08] >>> call(p, "Hello world!") [22:08] Hello world! [22:09] oh, okay, i think i'm starting to get it. [22:10] nUboon2Age, just start to think of things as objects [22:10] a function is also an object [22:10] so it can be passed around [22:11] i'll go back and look at the prior examples to see if i understand them now. [22:11] nUboon2Age, are you good with math? [22:12] pedro3005: not great, just okay [22:12] i think this is the line i was confused by: x = f(x) [22:13] i guess the f(x) was not explicitly defined in the example [22:14] no, by that line we're calling the function that was passed to us [22:14] we don't know what the function does [22:14] we just expect it to return something [22:14] and we set x to be that [22:14] think of it like this [22:14] on that example, we're dealing with numeric functions [22:15] so they're supposed to take a number like 4 and return something like 3 [22:15] so imagine we have x = 4 [22:15] we want to call the function which does whatever and then set x to be that new value [22:15] x = f(x) [22:15] repeat this process until we get a negative value [22:16] okay i got that. [22:17] another thing that i was confused by was "return [f(x) for val in x]" -- the 'for val in x' part isn't clear to me pedro3005 [22:18] nUboon2Age, it was a mistake by me [22:18] oh, what was it supposed to say? [22:18] return [f(x) for x in values] [22:20] nUboon2Age, it kind of makes sense if you read it out loud. [f of x for (each) x in values] [22:20] okay, could you break that down for me please pedro3005? [22:20] so what would an example be pedro3005? [22:21] grab each x inside values and add to the new list we're creating the return value of f(x) [22:21] hm, for instance [22:21] [x for x in [1, 2, 3]] [22:21] that is simply [1, 2, 3] [22:21] for each x in the old list, it puts x in the new list [22:21] but imagine this [22:21] [str(x) for x in [1, 2, 3]] [22:22] that is ['1', '2', '3'] [22:22] for x in the old list, it puts string of x in the new list [22:22] for each* [22:22] similarly, we can do [22:22] [x for x in [-1, 0, 1] if x > 0] [22:22] does x mean each element in the array? [22:23] for each x in the old list, put x in the new list if x is bigger than 0 [22:23] nUboon2Age, yes, we're iterating through it [22:24] and x could be any label, not just x? [22:25] [goomba for goomba in [-1,0,1]if goomba > 0] -- pedro3005? [22:25] nUboon2Age, absolutely [22:25] i'm closer but not quite there pedro3005 [22:26] nUboon2Age, do you understand for loops? [22:26] yes [22:26] the thing that is confusing me i think is [22:26] the first x [22:26] >>> a = [1, 2, 3, 4, 5] [22:26] >>> new_a = [] [22:26] >>> for number in a: [22:26] ... new_a.append(number) [22:26] ... [22:26] >>> new_a [22:27] [1, 2, 3, 4, 5] [22:27] that is the same thing as saying new_a = [x for x in a] [22:27] so i understand ... for x in[blah], but why the first x? [22:28] nUboon2Age, well, again, read it out loud. for x in [blah]. imagine you're the interpreter. Okay, we've got that; for x in [blah]. but what do I do with it? [22:28] oh, i'm starting to get it [22:28] x for x in [blah] -- okay, just return x. [f(x) for x in [blah]] -- okay, call f() with x and return that [22:31] pedro3005: are you saying that x for x in [blah] would just return x? [22:32] nUboon2Age, well, it would grab each x in blah and return x to the new list [22:33] pedro3005: so z for x in [blah] would grab each x in blah and return z to the new list ? [22:33] there are like, two dudes, one building the new list and the other dude tells the first one what to put in. you're that dude. so you read x for x, pick up 4 and just tell him: "4!". but if you read f(x), you call f with x, and get, for instance, 3. So you tell him "3!" [22:33] nUboon2Age, no, that would be an error [22:33] ? [22:33] that wouldn't work [22:34] do they have to be the same variable pedro3005? [22:34] nUboon2Age, yes. read it: for each x in [blah], put z in the new list. that doesn't make sense, does it? [22:35] list comprehension is a way of constructing a new list with a certain set of instructions [22:35] pedro3005: unfortunately it makes sense to me ;-) [22:35] nUboon2Age, well, be the interpreter: WTF is z? [22:35] ;-) [22:35] if you try that, it would raise a WTFError [22:36] i'd have to define it elsewhere i guess i was assuming [22:36] well, actually a NameError [22:36] NameError: name 'z' is not defined [22:36] :-) [22:36] nUboon2Age, the variable inside the list comprehension is temporary [22:36] like uhm.. [22:36] hmmm... okay. [22:37] imagine your mom tells you this: grab each thing in this box. if this thing is a spoon, put it in the kitchen. if it's a dead animal throw it away [22:37] you gave me a good laugh there pedro3005 [22:38] what is 'thing'? [22:38] okay so how would you write that in python? [22:38] well, you're iterating over each item of the box and calling that 'thing' [22:39] thing for thing in box[spoon,dead animal]? [22:39] to_the_kitchen = [thing for thing in box if thing == spoon] [22:39] remember, you're building a new list [22:40] okay, I have a better example [22:40] the box is full of apples and oranges [22:40] to_the_kitchen = [thing for thing in box[spoon,dead animal] if thing == spoon] pedro3005? [22:40] your dad tells you: take two boxes, label them apples and the other oranges. grab each fruit in the old box. if it's an apple, put it in the apples box, and put it in the oranges box otherwise [22:41] apples = [fruit for fruit in box if fruit == apple] (this is an example, the variables box and apple are NOT defined, and thus would FAIL) [22:41] oranges = [fruit for fruit in box if fruit == orange] [22:42] okay, and if you were to complete the example to that it would not fail what would that look like? [22:43] s/to/so [22:43] >>> apple = "apple" [22:43] >>> orange = "orange" [22:43] >>> box = [apple, orange, orange, apple, orange, orange, apple, apple, apple, orange] [22:43] >>> apples = [fruit for fruit in box if fruit == apple] [22:43] >>> oranges = [fruit for fruit in box if fruit == orange] [22:43] >>> apples [22:43] ['apple', 'apple', 'apple', 'apple', 'apple'] [22:43] >>> oranges [22:43] ['orange', 'orange', 'orange', 'orange', 'orange'] [22:44] okay i get that. i' [22:44] i'll save that example [22:44] muchas gracias pedro3005 [22:44] nUboon2Age, :) now do homework :P [22:45] pedro3005: first i have to get through the rest of the examples. ;-) [22:46] nUboon2Age, and I will try to come up with better examples next time [22:48] pedro3005: math examples sometimes elude me, so for me this example made more sense. [22:49] nUboon2Age, yeah, sorry, I didn't think of those not good with abstract math [22:50] pedro3005: is the word 'global' predefined in python? [22:52] nUboon2Age, yes, it is a keyword [22:54] nUboon2Age, don't worry too much about global though [22:55] it's not very common to use [22:55] normally you'll just pass variables back and forth [22:56] in the above examples i didn't really understand what the map() is about pedro3005 [22:57] nUboon2Age, ah, a little relic from functional programming [22:57] here's what it does [22:57] it receives two parameters, a function and a list of values [22:57] it returns a list with the returned value by calling this function with each value === yofel_ is now known as yofel [22:58] like, if you call map(x, [a, b, c]) it returns [x(a), x(b), x(c)] [22:59] let me exemplify [23:00] suppose you have a list ['1', '2', '3'] [23:00] call that list snums [23:00] map(int, snums) [23:00] this calls int() with each element of snums [23:00] and returns a list substituting the value for the returned value of the function [23:00] in this case [1, 2, 3] [23:01] okay, that kinda makes sense. i'll have to ponder it for a little while pedro3005 [23:06] okay i'm getting that now pedro3005 [23:07] nUboon2Age, these are not trivial concepts, so it's normal to take a while to comprehend [23:19] one thing that i'm kinda surprised by is the seemingly large number of standard predefined functions. i'm used to languages where there are not very many pedro3005 [23:20] nUboon2Age, well, there are mere programming languages, and there's python [23:20] python is not a language, it's a lifestyle! [23:20] :P [23:21] python's standard library is ginormous [23:22] So big my class won't go over a tiny fraction of it [23:23] besides the built-in functions (the ones you can access without importing anything), which already are many, there are loads and loads of modules [23:24] think of anything you can do with a computer [23:24] python has a standard module for that [23:37] nUboon2Age, in conclusion, python rocks, love it [23:40] pedro3005: i'm happy to be learning python and it seems really fun, so thank you for your instruction. ;-) [23:41] nUboon2Age, is it your first language? [23:47] pedro3005: i've been away from programming for a little while, but [23:49] basic was my first language, followed much latter by pascal, C, turbo pascal with objects, C++, all in school, and then i taught myself Java [23:49] oh, and i had a little assembly too. [23:50] ah, cool [23:50] but i'm really rusty, so this is brushing my cobwebs off. ;-) [23:51] nUboon2Age, if you knew Java, classes shouldn't be too much of a problem [23:51] a little shell programming (and a fair bit of DOS batch files) too [23:51] pedro3005: yes, classes shouldn't be too bad. [23:52] nUboon2Age, python classes can inherit from various other ones ;) [23:52] i haven't gone as far as i'd like into OOP, so there may be some things that i'll need to wrestle with a bit. [23:53] good point pedro3005 [23:54] nUboon2Age, I think the biggest problem with python is how slow it is [23:58] pedro3005: yes that's an issue for every interpreted language. but i'm pleasantly surprised how reasonable the speed is of python. [23:58] oh, i forgot to mention i taught myself some perl too. [23:59] nUboon2Age, oh, I'm a language whore too [23:59] I go out learning a lot of different languages :P