Updates about python 3
Lambda expressions
The lamda expression
lambda [parameter_list]: expression
yields an anonymous function, which behaves in the same way as
def <lambda>(arguments):
return expression
useage example:
- uses a lambda expression to return a function
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))
is equal to
L = list(filter(lambda n: n % 2 == 1, range(1,20)))
- pass a small function as an argument
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')] >>> pairs.sort(key=lambda pair: pair[1]) >>> pairs [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
(see manual of list.sort())
higher-order function
In python, function can be be taken as arguments for other functions, and can also return as a result of other functions. That’s something I’ve met before, but the structure closure is something unfamiliar to me.
A closure is a way of keeping alive a variable even when the function has returned. In Python, this is done by nesting a function inside other function and then returning the underlying function.
def lazy_sum(*args):
def sum():
a = 0
for n in args:
a += n
return a
return sum
The good part about using closure here is that you need not to evaluate the sum immediately after providing your argument, see below
>>>f = lazy_sum(1, 3, 5, 7)
>>>f
<function closure_test.lazy_sum.<locals>.sum>
>>>f()
16
the f
references the variables of lazy_sum() even after it returns.
another useage:
def multiplier_of(n):
def multiplier(number):
return number*n
return multiplier
multiplywith5 = multiplier_of(5)
print(multiplywith5(9))
According to learnpython.org
ADVANTAGE : Closures can avoid use of global variables and provides some form of data hiding.(Eg. When there are few methods in a class, use closures instead)
It’s very important to note that in python, the variables in the enclosing scopes are only readonly by nested functions. However, one can use the nonlocal
keyword explicitly with these variables in order to modify them. Refer to examples of liaoxuefeng’s exercises if you please.
The nested functions also leads to the use of decorators.
decorators
decorator: A function returning another function, usually applied as a function transformation using the @wrapper
syntax.
generally, a decorator use syntax like
@decorator
def functions(arg):
...
and it is equivalent to:
def function(arg):
...
function=decorator(function)
Usage example 1:
adding @classmethod
before defining a method in class will transform that method into a classmethod
class C:
@classmethod
def f(cls, arg1, arg2, ...): ...
It can be called either on the class (such as C.f()) or on an instance (such as C().f()). The instance is ignored except for its class.
It is equal to
def f(self, arg1, arg2):...
f = classmethod(f)
Usage example 2:
a decorator is just another function which takes a functions and returns one, and you can use it to modify an old function and return a new one:
def Check(old_function):
def new_function(arg):
if arg<0: raise ValueError("Negative Argument")
old_function(arg)
return new_function
@Check
def r(n):
print(n**3)
and it will raise ValueError
then if you give r() a negative argument.
Usage example 3:
def type_check(correct_type):
def check(old_func):
def new_func(arg):
if not isinstance(arg, correct_type):
raise TypeError("Bad Type")
return old_func(arg)
return new_func
return check
@type_check(int)
def times2(num):
return num*2
@type_check(str)
def first_letter(word):
return word[0]
then when you test it, you can get expected results
print(times2(2)) # returns 4
times2('Not A Number') # raises TypeError
print(first_letter('Hello World')) # returns 'H'
first_letter(['Not', 'A', 'String']) # raises TypeError
More help available here and here, also the learnpython.org is quite helpful.
the use of @property
It’s also part of the decorator techniques, but relatively more complex.
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
According to Mr.Liao, when using @property
, we transfer a method to an attribute, and when we wrapped it up with this decorator, this descript object has extra methods: getter
, setter
, deleter
. Amazingly, these decorator act as decorators too. They return a new property object:
>>> property().getter(None)
<property object at 0x10ff079f0>
When we use @score.setter
, what we are doing is call that property().setter
method, so we are replacing the original setter function with the new one, and keep everything else.
For in-depth explanation, see answers in stackoverflow.
If we don’t designate that setter()
method, we’ll make the attribute readonly, which is, in some cases, what we desired.
class Screen(object):
@property
def width(self):
return self._width
@width.setter
def width(self, value):
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self, value):
self._height = value
@property
def resolution(self):
return self._width * self._height
more about list
we can use lists as stacks as well as queue, which makes it possible both “last-in, first-out” and “first-in, first-out”, although the latter is less efficient.
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
To implement a queue, use collections.deque
which was designed to have fast appends and pops from both ends.
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry") # Terry arrives
>>> queue.append("Graham") # Graham arrives
>>> queue.popleft() # The first to arrive now leaves
'Eric'
>>> queue.popleft() # The second to arrive now leaves
'John'
>>> queue # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])
list comprehension is definitely flexible:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
The sequence of for
in the second example should not be reversed, for the elem needs to be defined first by for elem in vec
.
In case I might forget, to unpack argument list, we use the *
operator, likewise, use **
-operator for dictionaries. See here
Looping through a sequence is easy wiht for
statements, and if we want the position index as well, we can use the enumerate()
function
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
to loop over two or more sequences, use zip
to pair, zip
yields an iterator
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
For more about zip
, see here
for the difference between list and tuple, the docs give us some hints:
Though tuples may seem similar to lists, they are often used in different situations and for different purposes. Tuples are immutable, and usually contain a heterogeneous sequence of elements that are accessed via unpacking or indexing (or even by attribute in the case of namedtuples). Lists are mutable, and their elements are usually homogeneous and are accessed by iterating over the list.