# Do you remember this code from previous lecture (lecture 2)
def make_adder(n=1):
return lambda x: x + n
one_adder = make_adder() # actually creates: one_adder = lambda x: x + 1
one_adder(1)
"""
1. List/Dict comprehension
"""
# [{do job using 'for' variable} {'for' cycle} {filter predicate (optional)}]
# for lists
i_all = [i for i in range(0, 10)]
i_all_even = [i for i in range(0, 10) if i % 2 == 0]
# for dict
x_squared_dict = {item: item * item for item in range(0, 4)}
"""
2. Map (aka transform) function
map: this function takes a unary function and some list/iterable
of values and produces a list/iterable of mapped/transformed values based on
substituting each value with the result of calling the parameter function
on that value.
map({function}, {iterable+}) returns transformed sequence
"""
map(lambda x : x**2, range(1, 5))
map(lambda x,y: x*y, range(1, 5), range(1, 5))
"""
3. Filter (aka accumulate)
filter: this function takes a predicate (a unary function returning a bool)
and some list/iterable of values and produces a list/iterable with only
those values for which the predicate returns True.
filter({predicate function}, {iterable})
"""
# filter({predicate for even}, [i for i in range(2,50)])
def is_even(x):
return x % 2 == 0
# Remember how function is passed
filter(is_even, [i for i in range(2,50)])
# Using lambda. Do not forget () around!
filter((lambda x: x % 2 == 0), [i for i in range(2,50)])
"""
4. Reduce
reduce: this function is different than the previous two: it
takes a binary function and some list/iterable of values and typically produces
a single value: it reduces or accumulates its arguments into a single value.
By default, the first item in the sequence initialized the starting value.
"""
# function with two parameters is a binary function
reduce((lambda x, y: x * y), range(1, 5))
# (((1 * 2) * 3) * 4)
# Here is the 'for' loop version
L = range(1, 5)
result = L[0]
for x in L[1:]:
result = result * x
print result
reduce(lambda x,y : x - y, [1, 2, 3])
# ((1 - 2) - 3)
# NOTE: Technically, this is called LEFT reduction/accumulation because the operators are
# applied left to right.
def max(x, y):
return x if x > y else y
# Then, calling
reduce(max, [4, 2, -3, 8, 6])
# is equivalent to max(max(max(max(4,2),-3),8),6) which evaluates (left to right)
# as follows, to compute the maximum of the entire list of values.
#
# max(max(max(max(4,2),-3),8),6) -> max(max(max(4,-3),8),6) ->
# max(max(4,8),6) -> max(8,6) -> 8
# Here is a concrete example of a function style of programming, using map,
# filter, and reduce.
# This expression computes the length of the longest line in
# a file.
reduce(max, map(lambda l: len(l.strip()), open('recursion.txt')))
# To return the longest line, not the length of the longest line, we could alter
# the computation slightly, as follows.
reduce(lambda x,y: x if len(x) >= len(y) else y,
map(lambda l: l.strip(), open('recursion.txt')))
# Collect all files that contains a word
filter(lambda x: 'Problems:' in x, # **fixed**
map(lambda l: l.strip(), open('recursion.txt'))) # **fixed**
# REMEMBER: Your functions should be as general as possible!
def search_in(file_name):
return filter(lambda x: 'Problems:' in x ,
map(lambda l: l.strip(), open(file_name)))
# not so bad - you can pass any file
search_for_prblems = search_in('recursion.txt')
search_for_prblems
# even better search for a different text
def search_in(file_name, text):
return filter(lambda x: text in x ,
map(lambda l: l.strip(), open(file_name)))
search_for = search_in('recursion.txt', 'Define')
search_for
# Could we divide file from text?
# In fist phase we set file_name in second text => closures baby!
# Returns a function(closure with file_name fixed) with one parameter
def search_in(file_name):
return lambda text: filter(lambda x: text in x ,
map(lambda l: l.strip(), open(file_name)))
search_for = search_in('recursion.txt')
search_for('Define')
# Tip: Think of map as equivalent to one 'for' cycle
# I. Let's write 'combine_all' from previous lecture homework in Lisp way
# 1. map(map({func(x, y)}, ylist), xlist)
# 2. map(map((lambda x,y: [x, y]), ylist), xlist)
# 3. Use closure to pre-set x:
# map((lambda x: map((lambda y: [x, y])), ylist)), xlist)
# 4. Check up to now
def combine_all(xlist, ylist):
return map((lambda x:
map((lambda y: [x, y]), ylist)),
xlist)
combine_all(['a', 'b'], [1, 2])
# return two lists: [[['a', 1], ['a', 2]], [['b', 1], ['b', 2]]]
# 5. flatten one level -> [[1, 2], [3, 4]] to become [1, 2, 3, 4]
import operator
def combine_all(xlist, ylist):
return reduce(operator.add, (map((lambda x:
map((lambda y: [x, y]), ylist)),
xlist)))
# II. Let's write 'combine_all' from previous lecture homework in Python way
# list comprehension could be multi-level
def combine_all(xlist, ylist):
return [[x, y] for x in xlist for y in ylist]
# Tip: Always use list comprehension when want to return list
'''
1. Kinds of iteration:
- for, while (iterators, generators)
- map (high order functions)
- recursion - we strive to be tail recursion(later in this lecture)
'''
'''
2. What is recursion?
Recursion is a programming technique in which a call to a function results in
another call to that same function. Recursion means "defining something in terms of itself".
It is not only for functions - also for structures e.g. lists:
list = element | list
'''
# How can we present a list? list = element + rest of list
lst = [1, 2, 3]
lst2 = [lst[0]] + lst[1:]
# TIP: Remember that lst[0] returns element not list so we have to convert it to list!
# Do you remember that?
a = 'abc'
a[0]
# How about strings? It is a bit easy:
s = 'abc'
s2 = s[0] + s[1:]
'''
3. Lists and recursion
If you have list(string is also a list) data structure you can use recursion.
'''
# Here is how we structure recursive function:
# Phase One - stop condition
def reverse(s):
if s == '':
return ''
else:
# Recur to solve a smaller problem
# Use the solution of the smaller problem to solve the original problem
pass
# Phase Two - use rest of the list s[1:]
def reverse(s):
if s == '':
return ''
else:
# Use the solution of reverse(s[1:]) to solve the original problem
pass
def reverse(s):
if s == '':
return ''
else:
return reverse(s[1:]) + s[0]
# Pythonic way using if
def reverse(s):
return '' if s == '' else reverse(s[1:]) + s[0]
# Example
reverse('abcd')
'''
2. What is tail recursion and why it is better?
'''
# Which one is better - iteration or recursion?
def recsum(x):
if x == 1:
return x
else:
return x + recsum(x - 1)
# Evaluation description
#
# recsum(5)
# 5 + recsum(4)
# 5 + (4 + recsum(3))
# 5 + (4 + (3 + recsum(2)))
# 5 + (4 + (3 + (2 + recsum(1))))
# 5 + (4 + (3 + (2 + 1)))
# 15
# Not tail recursive either
def bad_factorial(n):
if n == 0:
return 1
return n * bad_factorial(n - 1)
# Python limits the number of times any recursive function can call itself.
# Convert recursion to tail recursion introducing accumulator (aka pumping)
def factorial(n, accum=1):
if n == 0:
return accum
else:
return factorial(n - 1, n * accum)
# TCO (Tail Call Optimization) is the process by which a smart compiler can make a call to
# a function take no additional stack space.
# REMEMBER: Python do not do tail-call optimization!
# Read why: http://neopythonic.blogspot.pt/2009/04/tail-recursion-elimination.html
Thursday, June 25, 2015
Python Functional Programming
Posted by
Zlatozar
at
5:12 PM
0
comments
Labels: Python
Python Crash Course - part two
"""
1. Functions declaration
- Basic function definition
def name(parameter1, parameter2, . . .):
''' Function documentation (optional) '''
body
"""
# STYLE: It’s a standard practice for multi-line documentation strings to give a synopsis
# of the function in the first line (brief), follow this with a blank second line, and end
# with the rest of the information.
# You can obtain documentation string with: fact.__doc__
def fact(n):
"""Return the factorial of the given number."""
r = 1
while n > 0:
r = r*n
n = n - 1
return r
'''
A function that doesn’t return a value is called a procedure. All Python procedures are
functions; if no explicit return is executed in the procedure body, then the special
Python value None is returned.
'''
# Assign the result
x = fact(4)
'''
2. Function parameter options
The simplest way to pass parameters to a function in Python is by position. In the first
line of the function, you specify definition variable names for each parameter; when the
function is called, the parameters used in the calling code are matched to the function’s
parameter variables based on their order.
'''
# This method requires that the number of parameters used by the calling code exactly
# match the number of parameters in the function definition, or a TypeError exception will
# be raised:
def power(x, y):
r = 1
while y > 0:
r = r*x
y = y - 1
return r
'''
2.1 Default values
Function parameters can have default values, which you declare by assigning a default
value in the first line of the function definition, like so:
def fun(arg1, arg2=default2, arg3=default3, . . .)
Any number of parameters can be given default values.
NOTE: Parameters with default values must be defined as the _last_ parameters in the
parameter list.
'''
# STYLE: See there is not space in default parameters assignment
def power(x, y=2):
r = 1
while y > 0:
r = r * x
y = y - 1
return r
'''
2.2 Passing arguments by parameter name
This type of argument passing is called key- word passing.
'''
power(y=2, x=3)
# Keyword passing, in combination with the default argument capability of Python
# functions, can be highly useful when you’re defining functions with large numbers of
# possible arguments, most of which have common defaults.
# Suppose you have:
# def list_file_info(size=False, create_date=False, mod_date=False, ...):
# ...get file names...
# if size:
# # code to get file sizes goes here
# if create_date:
# # code to get create dates goes here
# ...
# ...
# ...
# return file_info_structure
# Hey do not worry - you do not need to remember all argument positions
#
# file_info = list_file_info(size=True, mod_date=True)
'''
2.3 Variable numbers of arguments
- One way handles the relatively familiar case where you wish to collect an unknown number
of arguments at the end of the argument list into a list.
- Also you can collect an arbitrary number of keyword-passed arguments, which have no
correspondingly named parameter in the function parameter list, into a dictionary.
'''
# Prefixing the _final_ parameter name of the function with a * causes all excess non-
# keyword arguments in a call of a function to be collected together and assigned as a
# tuple to the given parameter.
def maximum(*numbers):
if len(numbers) == 0:
return None
else:
maxnum = numbers[0]
for n in numbers[1:]:
if n > maxnum:
maxnum = n
return maxnum
# Here is how we call it:
maximum(2, 3, 4, 5)
# If the _final_ parameter in the parameter list is prefixed with **, it will collect all
# excess keyword-passed arguments into a dictionary. The index for each entry in the
# dictionary will be the keyword (parameter name) for the excess argument.
def example_fun(x, y, **other):
print("x: {0}, y: {1}, keys in 'other': {2}".format(x, y, list(other.keys())))
other_total = 0
for k in other.keys():
other_total = other_total + other[k]
print("The total of values in 'other' is {0}".format(other_total))
# Here is an example call:
example_fun(2, y="1", foo=3, bar=4)
'''
3. Mutable objects as arguments
REMEMBER: Arguments are passed in by object reference!
- Immutable objects (such as tuples, strings, and numbers)
What is done with a parameter has no effect outside the function.
- Mutable objects (for example, a list, dictionary, or class instance),
Any change made to the object will change what the argument is referencing outside the
function.
'''
# Example is always better:
def f(n, list1, list2):
list1.append("add new value")
list2 = ["a", "completely" , "new", "value"]
n = n + 1
x = 5 # for n
y = [1, 2] # for list1
z = [3, 4] # for list2. Be careful!!
f(x, y, z)
print x, y, z
'''
At the begining:
z list2
\ /
\ /
\ /
+-----------+
| [3, 4] |
+-----------+
'''
'''
4. Local and global variables
'''
# REMEMBER: Functions define scope!
# Any variables in the parameter list of a function, and any variables created within a
# function by an assignment (like r = 1 in fact), are local to the function.
# You can explicitly make a variable global by declaring it so before the variable is
# used, using the _global_ statement.
def fun():
global a # use top level
a = 1
b = 2
# Let's test it:
a = "one"
b = "two"
fun()
print a
print b
''''
5. Assigning functions to variables
Functions are first class objects!
'''
# Functions can be assigned, like other Python objects, to variables, as shown in the fol-
# lowing example:
def f_to_kelvin(degrees_f):
return 273.15 + (degrees_f - 32) * 5 / 9
def c_to_kelvin(degrees_c):
return 273.15 + degrees_c
abs_temperature = f_to_kelvin
abs_temperature(32)
abs_temperature = c_to_kelvin
abs_temperature(0)
# You can place them in lists, tuples, or dictionaries:
t = {'FtoK': f_to_kelvin, 'CtoK': c_to_kelvin}
t['FtoK'](32)
'''
6. lambda expressions (aka mini functions)
Taken directly from LISP!
lambda expressions are anonymous little functions that you can quickly define inline.
lambda argument1, argument2,... argumentN :expression using arguments
Why?
Often, a small function needs to be passed to another function.
'''
# Explain with examples:
import math
def square_root(x): return math.sqrt(x)
# with lambda (one parameter)
square_root = lambda x: math.sqrt(x)
# direct call
(lambda x: math.sqrt(x))(4)
# See more examples in next lecture be patient...
'''
7. Generator(or coroutine) functions
Why?
Generators functions allow you to declare a function that behaves like an iterator,
i.e. it can be used in a loop.
'''
# REMEMBER: Generators are memory efficient! Laziness helps for that :)
# Compare thies two implementations
def firstn(n):
num, nums = 0, []
while num < n:
nums.append(num)
num += 1
return nums
sum_of_first_n = sum(firstn(1000000))
def firstn(n):
num = 0
while num < n:
yield num
num += 1
sum_of_first_n = sum(firstn(1000000))
# How could you define function that return all nonnegative integers?
def all_naturals():
x = 0
while True:
yield x
x += 1
33 in all_naturals()
'''
8. Functions in functions - dynamically creating a function (aka lexical closures)
'''
# LEGB Rule - order matters
# L. Local. Names assigned in any way within a function (def or lambda)), and not
# declared global in that function.
# E. Enclosing function locals. Name in the local
# scope of any and all enclosing functions (def or lambda), form inner to outer.
# G. Global (module). Names assigned at the top-level of a module file, or declared global
# in a def within the file.
# B. Built-in (Python). Names preassigned in the built-in names
# module: Python "kernel"
# Do you remember that functions creates scope?
def make_adder(n=1):
def action(x): # inner functions could use all enclosing variables
return x + n
return action
# using lambda
def make_adder(n=1):
return lambda x: x + n
one_adder = maker_adder() #=> one_adder = lambda x: x + 1
one_adder(1)
# nested lambdas
one_adder = (lambda n: lambda x: x + n)(1)
# NOTE: You can't reference inner function directly
# With nested functions (closures) you create a function with state (initial value)
two_adder = make_adder(2)
two_adder(1)
'''
9. Decorators
A decorator is just a callable that takes a function as an argument and returns a
replacement function.
'''
def logger(func):
def inner(*args, **kwargs): # all kind of parameters
print "Arguments were: %s, %s" % (args, kwargs)
return func(*args, **kwargs) # call 'real' function!
return inner
# Here is how to use it:
@logger
def foo(x, y=1):
return x * y # logger(foo(30))
foo(30)
'''
10. Multi-value return
'''
def divide(x, y):
quotient = x / y
remainder = x % y
return quotient, remainder
all = divide(22, 7)
all, _ = divide(22, 7)
q, r = divide(22, 7)
'''
11. __ and __{some}__ functions
Naming convention:
_ for protected
__ for private
'''
Posted by
Zlatozar
at
4:43 PM
0
comments
Labels: Python
Python Crash Course - part one
Lecture 2
Lecture 3 (functional programming)
"The quality of our thoughts is bordered on all sides
by our facility with language."
--J. Michael Straczynski
# How to comment?
# What is a data type?
'''
1. Variables, assignments and expressions
- Neither a variable type declaration nor an end-of-line delimiter is necessary.
- The line is ended by the end of the line.
- Variables are created automatically when they're first assigned.
- A new assignment overrides any previous assignments.
- Assignment do not return value.
'''
x = "Hello"
print x
del x
# print x and read the traceback
# Expressions return values
x = 3
y = 5
z = (x + y) / 2
"""
2. Indentation and block structuring
Python doesn't use braces to indicate code structure; instead,
the indentation itself is used.
- It's impossible to have missing or extra braces.
- Easy to grasp the skeleton of code just by looking at it.
- Python coding styles are mostly uniform – no flavour wars!
"""
# NOTE: With assignment you include time!
# You say: "That is the value at *this* moment". This is dangerous - SICP for more details.
n = 9
r = 1
while n > 0:
r = r * n
n = n - 1
"""
3. Strings
Like most other programming languages, indicates strings through
the use of " or '
- You can't split a normal string across lines - use triple-quoted strings.
"""
# Backslashes can be used to escape characters, to give them special meanings.
#
# \n means the newline character
# \t means the tab character
# \\ means a single normal backslash character
# \" is a plain double-quote character
# Use 'print x' do not type only x
x = "\tDon't need a backslash\n"
x = 'Can\'t get by without a backslash'
x = "Backslash your \" character!"
x = 'You can leave the " alone'
"""
4. Numbers
- Python offers four kinds of numbers: integers, floats, complex numbers, and Booleans.
- Integers can be arbitrarily large
- The division of two integers results in a float
- Built-in numeric functions:
abs, divmod, cmp, coerce, float, hex, int, long, max, min, oct, pow, round
"""
# This is impossible integer in Java
x = 30000000000
x = x / 2.3
# Build in numeric functions
round(2.3)
# Booleans: True, False
# Most Python objects can be used as Booleans!
# The numbers 0, 0.0, and 0+0j are all False; any other number is True.
# The empty string "" is False; any other string is True.
# The empty list [] is False; any other list is True.
# The empty dictionary {} is False; any other dictionary is True.
# The empty set set() is False; any other set is True.
# The special Python value None is always False.
"""
5. 'None' value
- Like zero in arithmetic
This is enough for now.
"""
"""
6. Lists are like arrays
- Ordered collection of objects
- Lists can contain different types of elements
NOTE: Lists are recursive structure - list = element | list
"""
# Note that you don't have to worry about declaring the list
# or fixing its size ahead of time. Following example creates the list
# as well as assigns it, and a list automatically grows or shrinks in size as needed.
# empty one
x = []
# This assigns a three-element list to x
x = [1, 2, 3]
# First element is a number, second is a string, third is another list.
x = [2, "two", [1, 2, 3]]
# Python indexing is more flexible than C indexing; if indices are negative
# numbers, they indicate positions counting from the end of the list,
# with –1 being the last position in the list, –2 being the second-to-last position,
# and so forth.
x = ["first", "second", "third", "fourth"]
a = x[-1]
b = x[-2]
help([]) # or help(list)
"""
7. List slicing
"""
# Can extract or assign to an entire sublist at once, an operation known as slicing.
# Instead of entering list[index] to extract the item just after index,
# enter list[index1:index2] to extract all items including index1
# and up to (but not including) index2 into a new list.
x = ["first", "second", "third", "fourth"]
x[1:-1]
x[0:3]
x[-2:-1]
# When slicing a list, it's also possible to leave out index1 or index2.
# Leaving out index1 means "go from the beginning of the list,"
# and leaving out index2 means "go to the end of the list":
x[:3]
x[2:]
# What about x[:]?
"""
8. Modifying lists
- You can use list index notation to modify a list as well as to extract
an element from it.
- You can also use slicing
"""
x = [1, 2, 3, 4]
x[1] = "two"
# slicing
x = [1, 2, 3, 4]
x[len(x):] = [5, 6, 7]
# NOTE: len() is generic function for all type of sequences
# append at the beginning
x[:0]
# append at the end
x = [1, 2, 3]
x.append("four")
# if append list?
y = [5, 6, 7]
x.append(y)
x = [1, 2, 3]
x.extend(y)
# or use '+'
z = [1, 2, 3] + [4, 5]
# insertion
x = [1, 2, 3]
x.insert(2, "hello")
x.insert(0, "start")
# The 'del' statement is the *preferred* method of deleting list items or slices
x = ['a', 2, 'c', 7, 9, 11]
del x[1]
del x[:2]
# As a homework read about 'remove'
"""
9. List membership with the in operator
- To test if a value is in a list using the 'in' operator, which returns a Boolean value.
"""
3 in [1, 3, 4, 5]
3 not in [1, 3, 4, 5]
3 not in ["one", "two", "three"]
"""
10. List initialization with the * operator
"""
z = [None] * 4
z = [3, 1] * 2 # list multiplication operator
"""
11. Tuples
Tuples are data structures that are very similar to lists, but they
can't be modified. They can only be created.
- The main difference between tuples and lists is that tuples are immutable.
"""
x = ('a', 'b', 'c')
# REMEMBER: One-element tuples need a comma!
x = 3
y = 4
(x+y) # adds x and y
(x + y,) # Including a comma indicates the parentheses denote a tuple
"""
12. Packing and unpacking tuples
"""
(one, two, three, four) = (1, 2, 3, 4)
# even simpler
one, two, three, four = 1, 2, 3, 4
"""
13. Converting between lists and tuples
It is important to stay immutable!
"""
list((1, 2, 3, 4))
tuple([1, 2, 3, 4])
# 'list' is a convenient way to break a string into characters
list("Hello")
"""
14. Sets
A set in Python is an unordered collection of objects used in situations where member-
ship and uniqueness in the set are main things you need to know about that object.
ATTENTION: Just as with dictionary keys (as you'll see later), the items in a set
must be immutable and hashable. This means that ints, floats, strings, and tuples can be
members of a set, but lists, dictionaries, and sets themselves can't
"""
# sets also have several set-specific operations
x = set([1, 2, 3, 1, 3, 5])
y = set([1, 7, 8, 9])
# union
x|y
# intersection
x&y
# not in both - symmetric difference
x^y
# Frozensets - Because frozensets are immutable and hashable, they can be members of other
# sets:
x = set([1, 2, 3, 1, 3, 5])
z = frozenset(x)
x.add(z)
"""
15. Strings
Strings can be considered sequences of characters, which means you can use index or slice
notation.
REMEMBER: Strings are immutable!
"""
x = "Hello"
x[0]
len(x)
8 * "Победа! "
"""
16. String methods
"""
" ".join(["join", "puts", "spaces", "between", "elements"])
x = "You\t\t can have tabs\t\n \t and newlines \n\n mixed in"
x.split()
help(split) # error
help("".split()) # something different
help("".split)
# Legacy code: string.split(text)
# Converting strings to numbers
float('123.456')
# Getting rid of extra whitespace
x = " Hello, World\t\t "
x.strip()
x.lstrip()
x.rstrip()
# String searching
x = "Mississippi"
# 'find' takes one required argument: the substring being searched for. 'find' returns the
# position of the first character of the first instance of substring in the string object,
# or –1 if substring doesn't occur in the string
x.find("ss")
help("".find)
# with optional parameter
x.find("ss", 3)
# count is used identically to any of the previous four functions but returns the number
# of non-overlapping times the given substring occurs in the given string
x.count("ss")
x.startswith("Miss")
x.endswith("pi")
"""
17. Modifying strings?!
Strings are immutable, but string objects have a number of methods that can operate on
that string and return a new string that's a modified version of the original string.
"""
x.replace("ss", "+++")
# Cool - Modifying strings with list manipulations
text = "Hello, World"
# Make it mutable list
wordList = list(text)
wordList[6:] = []
wordList.reverse()
text = "".join(wordList)
print text
"""
18. Using the format method
The format command is a powerful string-formatting mini-language and offers almost endless
possibilities for manipulating string formatting.
"""
# The format method and positional parameters
"{0} is the {1} of {2}".format("Ambrosia", "food", "the gods")
# The format method and named parameters
"{food} is the food of {user}".format(food="Ambrosia", user="the gods")
# ... it is powerful
"{0} is the food of {user[1]}".format("Ambrosia", user=["men", "the gods", "others"])
# The format specifier lets you control the fill character, alignment, sign, width,
# precision, and type of the data when it's substituted for the replacement field.
# will skip format specifiers...
# Formatting strings with %
"%s is the %s of %s" % ("Ambrosia", "food", "the gods")
"""
19. Dictionaries
Dictionaries are also called associative arrays or hashtables in other languages.
"""
# Empty dictionary is created much like an empty list, but with curly braces instead of
# square brackets
x_list = []
y_dict = {}
help(dict)
# Keys
x = {0: 'zero', 1: 'one'}
# Whereas list indices must be integers, dictionary keys are much less restricted -
# they may be numbers, strings, or one of a wide range of other Python objects.
y = {}
y["two"] = 2
y["pi"] = 3.14
y["two"] * y["pi"]
len(y)
english_to_french = {'red': 'rouge', 'blue': 'bleu', 'green': 'vert'}
# keys in a list
list(english_to_french.keys())
# values in a list
list(english_to_french.values())
# both
list(english_to_french.items())
# 'in' checks in keys
'red' in english_to_french
# Attempting to access a key that isn't in a dictionary is an error in Python!
# Alternatively, you can use the 'get' function. It returns the value associated with a key,
# if the dictionary contains that key, but returns its second argument if the dictionary
# doesn't contain the key:
print english_to_french.get('chartreuse', 'No translation')
# The difference between 'get' and 'setdefault' is that after the setdefault call, there
# is a key in the dictionary 'chartreuse' with the value 'No translation'.
print english_to_french.setdefault('chartreuse', 'No translation')
# What can be used as a key?
# Any Python object that is immutable and hashable can be used as a key to a dictionary!
"""
20. Control flow
"""
# WHILE LOOP
# while condition: <-- : means that control structure continues
# body
# else: <-- difference is here (optional)
# post-code
# 'condition' is an expression that evaluates to a true or false value. As long as it's
# True, the 'body' will be executed repeatedly. If it evaluates to False, the while loop
# will execute the 'post-code' section and then terminate.
# The break and continue statements in body or pass
# The IF-ELIF-ELSE statement
# if condition1:
# body1
# elif condition2:
# body2
# elif condition3:
# body3
# .
# .
# elif condition(n-1):
# body(n-1)
# else:
# body(n)
# The FOR loop
# for item in sequence:
# body
# else:
# post-code <-- optional
x = [1, 3, -7, 4, 9, -5, 4]
for i in range(len(x)): # range function
if x[i] < 0:
print "Found a negative number at index: %s" % i
# enumerate function to loop over both the items and their index
x = [1, 3, -7, 4, 9, -5, 4]
for i, n in enumerate(x): # enumerate
if n < 0:
print "Found a negative number at index: %s " % i
# Comparison and Boolean operators: <, <=, >, >=, ==, !=, <>, or, and
# The big example :)
def contains_even_number(l):
"Prints whether or not the list l contains an even number."
for elt in l:
if elt % 2 == 0:
print "list contains an even number"
break
else:
print "list does not contain an even number"
Posted by
Zlatozar
at
4:41 PM
0
comments
Labels: Python
Saturday, December 21, 2013
Update JSON leaf value using Python
От някоко месеца се опитвам да направя продукт (казва се PyTak), чрез която лесно да се правят сценарии
за тесване на REST API и тяхното оркестиране. Целия код може да си го свалите от GitHub
Получава се добре за сега - Python-a си покза силата. Ето един примерен (от лесните):
def main():
login()
scenario = [
CreateTag(assign={"name" : "new-tag"}),
DeleteTag(bind={"name" : "0.data.name"})
]
run(scenario)
validate(CreateTag(), {'xy.1.y.1.y2.0.y22': 3})
Нищо работа - създаваме tag с име new-tag и после го изтриваме като използваме върнатия резултат. Трудността идва, когато трябва да се промени request body.
За целта предефинирах <<. Целта е максимално да се улесни нашия QA Team. Примерен фрагмент:
create_post = CreateAPost() << { "title" : "New Employee - [XXXX]", "body" : "Please welcome our new employee. [XXXX]", "type" : "TEXT", "tags" : [{"name" : "tag2"}, {"name" : "tag3"}, {"name" : "tag4"}] }
По време на тестването често се налага да се слагат случайни стойности, за да се избегнат колизии. Въведох правилото - ако някой постави [XXXX] да се замени автоматично с случайно число.
От тук се получи задачата, която искам да споделя.
Да се заменят вскички стойности в JSON, които съдържат дадена стрингова стойност
Важно е да се уточни, че пропускаме ключове, чиито стойност е масив и обхождаме ключовете в самия масив. Ето моето решение, в което използвам mutual recursion.
import re
def extract_paths(json):
"""Select all JSON keys using mutual recursion"""
_path = []
_query = []
def __form_path(in_dict, in_list):
if _path:
_query.append(_path[:])
del _path[:]
if in_list:
for el in in_list:
_path.append(el)
if in_dict:
for el in in_dict:
_path.append(el)
def dict_update(json, in_list=None, in_dict=None):
if isinstance(json, dict):
for key, value in json.iteritems():
_path.append(key)
dict_update(value, in_dict=_path[:-1])
if isinstance(json, list):
list_update(json, in_list=_path[:-1])
else:
__form_path(in_dict, in_list)
return _query
def list_update(inner_list, in_list=None, in_dict=None):
if isinstance(inner_list, list):
for idx, value in enumerate(inner_list):
_path.append(idx)
list_update(value, in_list=_path[:-1])
if isinstance(inner_list, dict):
dict_update(inner_list, in_dict=_path[:-1])
else:
__form_path(in_dict, in_list)
return _query
return dict_update(json)
def update_leaf_if_value_func(json, change_func):
"""Apply 'change_func' to every leaf using the leaf as an argument"""
all_leafs = [path for path in extract_paths(json)]
for query in all_leafs:
selected = __upto_key_for_change(json, query)
if isinstance(selected[query[-1]], dict) or isinstance(selected[query[-1]], list):
continue
selected[query[-1]] = change_func(selected[query[-1]])
return json
def update_leaf_if_value(json, value_pattern, new_value):
"""Replace 'value_pattern' in a JSON leaf with new_value"""
all_leafs = [path for path in extract_paths(json)]
for query in all_leafs:
selected = __upto_key_for_change(json, query)
if isinstance(selected[query[-1]], dict) or isinstance(selected[query[-1]], list):
continue
selected[query[-1]] = re.sub(value_pattern, new_value, selected[query[-1]])
return json
def update(json, key, new_value):
"""Update *all* JSON keys that match 'key' with a given 'value'
Algorithm explanation:
1. Select path to JSON key referred by the given key
2. Fold the JSON structure up to this key (not included)
3. Update the leaf of folded JSON using given key
"""
matched = [path for path in extract_paths(json) if path[-1] == key]
for query in matched:
selected = __upto_key_for_change(json, query)
selected[query[-1]] = new_value
return json
def update_leaf(json, key, new_value):
"""Update *all* JSON leafs that match 'key' with a given 'value'
Algorithm explanation:
1. Select path to JSON leaf referred by the given key
2. Fold the JSON structure up to this key (not included)
3. Update the leaf of folded JSON using given key
"""
matched = [path for path in extract_paths(json) if path[-1] == key]
for query in matched:
selected = __upto_key_for_change(json, query)
# skip keys that refer dictionary or list
if isinstance(selected[query[-1]], dict) or isinstance(selected[query[-1]], list):
continue
# update only leafs
selected[query[-1]] = new_value
return json
#___________________________________________________________
# Helper functions
def __upto_key_for_change(data, query):
def __select(data, query):
return data[query]
return reduce(__select, query[:-1], data)
Ще стане добре - сигурен съм. Ако ми остане време ще разкрия и още интересни части от този проект, който е само в зародиш.
Posted by
Zlatozar
at
12:51 PM
0
comments
