├── ch1 ├── example │ ├── run.py │ ├── core.py │ └── util │ │ ├── db.py │ │ ├── __init__.py │ │ ├── math.py │ │ └── network.py ├── files_only │ ├── core.py │ ├── db.py │ ├── math.py │ ├── run.py │ └── network.py ├── scopes1.py ├── names.py ├── scopes2.py ├── scopes3.py └── bike.py ├── ch4 ├── lib │ ├── __init__.py │ └── funcdef.py ├── func_from.py ├── key.points.argument.passing.py ├── arguments.positional.py ├── arguments.keyword.py ├── func_import.py ├── arguments.defaults.mutable.no.trap.py ├── key.points.mutable.py ├── filter.lambda.py ├── key.points.assignment.py ├── matrix.multiplication.nofunc.py ├── vat.function.py ├── arguments.default.py ├── recursive.factorial.py ├── arguments.variable.keyword.py ├── return.none.py ├── arguments.variable.positional.unpacking.py ├── filter.regular.py ├── key.points.mutable.assignment.py ├── return.single.value.py ├── scoping.level.1.py ├── arguments.all.py ├── arguments.defaults.mutable.py ├── return.single.value.2.py ├── scoping.level.2.py ├── arguments.default.error.py ├── arguments.defaults.mutable.intermediate.call.py ├── matrix.multiplication.func.py ├── no.side.effects.py ├── scoping.level.2.global.py ├── scoping.level.2.nonlocal.py ├── vat.py ├── arguments.all.kwonly.py ├── arguments.variable.positional.py ├── arguments.variable.db.py ├── func.attributes.py ├── arguments.keyword.only.py ├── data.science.example.py ├── lambda.explained.py ├── return.multiple.py ├── docstrings.py └── primes.py ├── ch10 ├── regex │ ├── regex │ │ ├── __init__.py │ │ ├── wsgi.py │ │ ├── urls.py │ │ └── settings.py │ ├── entries │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ └── 0001_initial.py │ │ ├── templates │ │ │ └── entries │ │ │ │ ├── footer.html │ │ │ │ ├── insert.html │ │ │ │ ├── home.html │ │ │ │ ├── base.html │ │ │ │ └── list.html │ │ ├── forms.py │ │ ├── models.py │ │ ├── admin.py │ │ ├── static │ │ │ └── entries │ │ │ │ └── css │ │ │ │ └── main.css │ │ └── views.py │ ├── db.sqlite3 │ └── manage.py ├── flask │ ├── templates │ │ └── main.html │ └── main.py └── falcon │ ├── main.py │ ├── stress.py │ └── quotes.py ├── ch12 ├── pwdweb │ ├── pwdweb │ │ ├── __init__.py │ │ ├── wsgi.py │ │ ├── urls.py │ │ └── settings.py │ ├── records │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ └── 0001_initial.py │ │ ├── templates │ │ │ └── records │ │ │ │ ├── footer.html │ │ │ │ ├── messages.html │ │ │ │ ├── home.html │ │ │ │ ├── record_confirm_delete.html │ │ │ │ ├── base.html │ │ │ │ ├── list.html │ │ │ │ └── record_add_edit.html │ │ ├── admin.py │ │ ├── templatetags │ │ │ └── record_extras.py │ │ ├── forms.py │ │ ├── static │ │ │ └── records │ │ │ │ ├── css │ │ │ │ └── main.css │ │ │ │ └── js │ │ │ │ └── api.js │ │ ├── urls.py │ │ ├── models.py │ │ └── views.py │ ├── db.sqlite3 │ └── manage.py └── pwdapi │ ├── main.py │ ├── core │ ├── handlers.py │ └── passwords.py │ └── tests │ └── test_core │ ├── test_handlers.py │ └── test_passwords.py ├── requirements ├── requirements_dev.txt ├── requirements_test.in ├── requirements.in ├── requirements_test.txt └── requirements.txt ├── README.pdf ├── ch11 ├── traceback_simple.py ├── assertions.py ├── custom.py ├── traceback_validator.py ├── ipdebugger.py ├── custom_timestamp.py ├── ipdebugger_ipdb.py └── log.py ├── ch3 ├── conditional.1.py ├── permutations.py ├── simple.for.3.py ├── simple.for.py ├── simple.for.4.py ├── conditional.2.py ├── multiple.sequences.zip.py ├── infinite.py ├── multiple.sequences.enumerate.py ├── simple.for.2.py ├── multiple.sequences.py ├── primes.else.py ├── binary.2.py ├── multiple.sequences.while.py ├── multiple.sequences.explicit.py ├── range.py ├── multiple.sequences.implicit.py ├── ternary.py ├── for.else.py ├── taxes.py ├── switch.py ├── compress.py ├── for.no.else.py ├── switch.js ├── any.py ├── primes.py ├── binary.py ├── discount.py ├── coupons.dict.py ├── errorsalert.py └── coupons.py ├── ch5 ├── scopes.for.py ├── scopes.noglobal.py ├── gen.filter.py ├── gen.yield.from.py ├── squares.comprehension.py ├── functions.py ├── gen.yield.for.py ├── sum.example.py ├── pairs.list.comprehension.py ├── squares.py ├── dictionary.comprehensions.duplicates.py ├── dictionary.comprehensions.positions.py ├── sum.example.2.py ├── gen.map.py ├── pairs.for.loop.py ├── set.comprehensions.py ├── maxims.py ├── gen.send.preparation.py ├── dictionary.comprehensions.py ├── even.squares.py ├── gen.send.preparation.stop.py ├── filter.py ├── first.n.squares.py ├── fibonacci.elegant.py ├── gen.map.filter.py ├── gen.yield.return.py ├── scopes.py ├── gen.send.py ├── pythagorean.triple.comprehension.py ├── pythagorean.triple.py ├── fibonacci.first.py ├── zip.grades.py ├── fibonacci.second.py ├── pythagorean.triple.int.py ├── first.n.squares.manual.method.py ├── squares.map.py ├── performances.map.py ├── first.n.squares.manual.py ├── pythagorean.triple.generation.py ├── generator.expressions.py ├── pythagorean.triple.generation.for.py ├── map.example.py ├── decorate.sort.undecorate.py └── performances.py ├── ch8 ├── simple_server │ ├── serve.sh │ ├── img │ │ ├── owl-book.png │ │ ├── owl-books.png │ │ ├── owl-ebook.jpg │ │ ├── owl-rose.jpeg │ │ └── owl-alcohol.png │ └── index.html ├── scrape.py └── guiscrape.py ├── ch7 ├── data.py ├── exceptions │ ├── json.example.py │ ├── multiple.except.py │ ├── try.syntax.py │ ├── for.loop.py │ └── first.example.py ├── filter_funcs_triangulation.py ├── filter_funcs.py ├── filter_funcs_refactored.py ├── data_flatten.py └── profiling │ └── triples.py ├── ch2 ├── README.md ├── bytearray.py ├── objects.py ├── defaultdict.py ├── tuples.py ├── namedtuple.py ├── chainmap.py ├── final_considerations.py ├── sets.py ├── sequences.py ├── lists.py ├── dicts.py └── numbers.py ├── ch6 ├── decorators │ ├── time.measure.start.py │ ├── time.measure.dry.py │ ├── time.measure.arguments.py │ ├── time.measure.deco1.py │ ├── time.measure.deco2.py │ ├── decorators.factory.py │ ├── syntax.py │ └── two.decorators.py ├── oop │ ├── class.self.py │ ├── simplest.class.py │ ├── class.init.py │ ├── class.price.py │ ├── mro.py │ ├── super.duplication.py │ ├── mro.simple.py │ ├── operator.overloading.py │ ├── class.methods.factory.py │ ├── private.attrs.py │ ├── class.attribute.shadowing.py │ ├── private.attrs.fixed.py │ ├── super.explicit.py │ ├── class.namespaces.py │ ├── super.implicit.py │ ├── class.methods.split.py │ ├── property.py │ ├── static.methods.py │ ├── class.inheritance.py │ ├── multiple.inheritance.py │ └── class.issubclass.isinstance.py └── iterators │ └── iterator.py ├── tests └── test_ch7 │ ├── test_filter_funcs_final.py │ ├── test_filter_funcs_final_triangulation.py │ ├── test_filter_funcs_is_positive_correct.py │ ├── test_filter_funcs_is_positive_better.py │ ├── test_filter_funcs_is_positive_loose.py │ ├── test_filter_funcs_refactored.py │ ├── test_filter_funcs.py │ └── test_data_flatten.py ├── LICENSE ├── ch9 └── example.ipynb └── README.md /ch1/example/run.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/example/core.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/example/util/db.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/files_only/core.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/files_only/db.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/files_only/math.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/files_only/run.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch4/lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/example/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/example/util/math.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/example/util/network.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch1/files_only/network.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch10/regex/regex/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch10/regex/entries/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch12/pwdweb/pwdweb/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch10/regex/entries/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements/requirements_dev.txt: -------------------------------------------------------------------------------- 1 | ipdb 2 | -------------------------------------------------------------------------------- /requirements/requirements_test.in: -------------------------------------------------------------------------------- 1 | nose 2 | nose-parameterized 3 | -------------------------------------------------------------------------------- /README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/README.pdf -------------------------------------------------------------------------------- /ch11/traceback_simple.py: -------------------------------------------------------------------------------- 1 | d = {'some': 'key'} 2 | key = 'some-other' 3 | print(d[key]) 4 | -------------------------------------------------------------------------------- /ch3/conditional.1.py: -------------------------------------------------------------------------------- 1 | late = True 2 | if late: 3 | print('I need to call my manager!') 4 | -------------------------------------------------------------------------------- /ch3/permutations.py: -------------------------------------------------------------------------------- 1 | from itertools import permutations 2 | print(list(permutations('ABC'))) 3 | -------------------------------------------------------------------------------- /ch4/func_from.py: -------------------------------------------------------------------------------- 1 | from lib.funcdef import square, cube 2 | 3 | print(square(10)) 4 | print(cube(10)) 5 | -------------------------------------------------------------------------------- /ch4/key.points.argument.passing.py: -------------------------------------------------------------------------------- 1 | x = 3 2 | def func(y): 3 | print(y) 4 | 5 | func(x) # prints: 3 6 | -------------------------------------------------------------------------------- /ch4/lib/funcdef.py: -------------------------------------------------------------------------------- 1 | def square(n): 2 | return n ** 2 3 | 4 | 5 | def cube(n): 6 | return n ** 3 7 | -------------------------------------------------------------------------------- /ch10/regex/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/ch10/regex/db.sqlite3 -------------------------------------------------------------------------------- /ch12/pwdweb/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/ch12/pwdweb/db.sqlite3 -------------------------------------------------------------------------------- /ch4/arguments.positional.py: -------------------------------------------------------------------------------- 1 | def func(a, b, c): 2 | print(a, b, c) 3 | 4 | func(1, 2, 3) # prints: 1 2 3 5 | -------------------------------------------------------------------------------- /ch5/scopes.for.py: -------------------------------------------------------------------------------- 1 | s = 0 2 | for A in range(5): 3 | s += A 4 | print(A) # prints: 4 5 | print(globals()) 6 | -------------------------------------------------------------------------------- /ch5/scopes.noglobal.py: -------------------------------------------------------------------------------- 1 | ex1 = [A for A in range(5)] 2 | print(A) # breaks: NameError: name 'A' is not defined 3 | -------------------------------------------------------------------------------- /ch8/simple_server/serve.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # start a simple HTTP Server 4 | python -m http.server 8000 5 | -------------------------------------------------------------------------------- /ch3/simple.for.3.py: -------------------------------------------------------------------------------- 1 | surnames = ['Rivest', 'Shamir', 'Adleman'] 2 | for surname in surnames: 3 | print(surname) 4 | -------------------------------------------------------------------------------- /ch4/arguments.keyword.py: -------------------------------------------------------------------------------- 1 | def func(a, b, c): 2 | print(a, b, c) 3 | 4 | func(a=1, c=2, b=3) # prints: 1 3 2 5 | -------------------------------------------------------------------------------- /ch4/func_import.py: -------------------------------------------------------------------------------- 1 | import lib.funcdef 2 | 3 | 4 | print(lib.funcdef.square(10)) 5 | print(lib.funcdef.cube(10)) 6 | -------------------------------------------------------------------------------- /ch10/regex/entries/templates/entries/footer.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templates/records/footer.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /ch3/simple.for.py: -------------------------------------------------------------------------------- 1 | for number in [0, 1, 2, 3, 4]: 2 | print(number) 3 | 4 | 5 | for number in range(5): 6 | print(number) 7 | -------------------------------------------------------------------------------- /ch8/simple_server/img/owl-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/ch8/simple_server/img/owl-book.png -------------------------------------------------------------------------------- /ch8/simple_server/img/owl-books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/ch8/simple_server/img/owl-books.png -------------------------------------------------------------------------------- /ch8/simple_server/img/owl-ebook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/ch8/simple_server/img/owl-ebook.jpg -------------------------------------------------------------------------------- /ch8/simple_server/img/owl-rose.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/ch8/simple_server/img/owl-rose.jpeg -------------------------------------------------------------------------------- /ch8/simple_server/img/owl-alcohol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learning-Python/HEAD/ch8/simple_server/img/owl-alcohol.png -------------------------------------------------------------------------------- /ch4/arguments.defaults.mutable.no.trap.py: -------------------------------------------------------------------------------- 1 | def func(a=None): 2 | if a is None: 3 | a = [] 4 | # do whatever you want with `a` ... 5 | -------------------------------------------------------------------------------- /ch3/simple.for.4.py: -------------------------------------------------------------------------------- 1 | surnames = ['Rivest', 'Shamir', 'Adleman'] 2 | for position, surname in enumerate(surnames): 3 | print(position, surname) 4 | -------------------------------------------------------------------------------- /ch4/key.points.mutable.py: -------------------------------------------------------------------------------- 1 | x = [1, 2, 3] 2 | def func(x): 3 | x[1] = 42 # this affects the caller! 4 | 5 | func(x) 6 | print(x) # prints: [1, 42, 3] 7 | -------------------------------------------------------------------------------- /ch7/data.py: -------------------------------------------------------------------------------- 1 | def get_clean_data(source): 2 | 3 | data = load_data(source) 4 | cleaned_data = clean_data(data) 5 | 6 | return cleaned_data 7 | -------------------------------------------------------------------------------- /ch3/conditional.2.py: -------------------------------------------------------------------------------- 1 | late = False 2 | if late: 3 | print('I need to call my manager!') #1 4 | else: 5 | print('no need to call my manager...') #2 6 | -------------------------------------------------------------------------------- /ch4/filter.lambda.py: -------------------------------------------------------------------------------- 1 | def get_multiples_of_five(n): 2 | return list(filter(lambda k: not k % 5, range(n))) 3 | 4 | 5 | print(get_multiples_of_five(50)) 6 | -------------------------------------------------------------------------------- /ch4/key.points.assignment.py: -------------------------------------------------------------------------------- 1 | x = 3 2 | def func(x): 3 | x = 7 # defining a local x, not changing the global one 4 | 5 | func(x) 6 | print(x) # prints: 3 7 | -------------------------------------------------------------------------------- /ch5/gen.filter.py: -------------------------------------------------------------------------------- 1 | cubes = [x**3 for x in range(10)] 2 | 3 | odd_cubes1 = filter(lambda cube: cube % 2, cubes) 4 | odd_cubes2 = (cube for cube in cubes if cube % 2) 5 | -------------------------------------------------------------------------------- /ch5/gen.yield.from.py: -------------------------------------------------------------------------------- 1 | def print_squares(start, end): 2 | yield from (n ** 2 for n in range(start, end)) 3 | 4 | for n in print_squares(2, 5): 5 | print(n) 6 | -------------------------------------------------------------------------------- /ch5/squares.comprehension.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | >>> [n ** 2 for n in range(10)] 4 | [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 5 | -------------------------------------------------------------------------------- /ch3/multiple.sequences.zip.py: -------------------------------------------------------------------------------- 1 | people = ['Jonas', 'Julio', 'Mike', 'Mez'] 2 | ages = [25, 30, 31, 39] 3 | for person, age in zip(people, ages): 4 | print(person, age) 5 | -------------------------------------------------------------------------------- /ch5/functions.py: -------------------------------------------------------------------------------- 1 | 2 | def gcd(a, b): 3 | """Calculate the Greatest Common Divisor of (a, b). """ 4 | while b != 0: 5 | a, b = b, a % b 6 | return a 7 | -------------------------------------------------------------------------------- /ch5/gen.yield.for.py: -------------------------------------------------------------------------------- 1 | def print_squares(start, end): 2 | for n in range(start, end): 3 | yield n ** 2 4 | 5 | for n in print_squares(2, 5): 6 | print(n) 7 | -------------------------------------------------------------------------------- /ch5/sum.example.py: -------------------------------------------------------------------------------- 1 | s1 = sum([n**2 for n in range(10**6)]) 2 | s2 = sum((n**2 for n in range(10**6))) 3 | s3 = sum(n**2 for n in range(10**6)) 4 | 5 | print(s1==s2==s3) 6 | -------------------------------------------------------------------------------- /ch5/pairs.list.comprehension.py: -------------------------------------------------------------------------------- 1 | 2 | items = 'ABCDE' 3 | pairs = [(items[a], items[b]) 4 | for a in range(len(items)) for b in range(a, len(items))] 5 | 6 | print(pairs) 7 | -------------------------------------------------------------------------------- /ch5/squares.py: -------------------------------------------------------------------------------- 1 | def square1(n): 2 | return n ** 2 # squaring through the power operator 3 | 4 | def square2(n): 5 | return n * n # squaring through multiplication 6 | -------------------------------------------------------------------------------- /ch4/matrix.multiplication.nofunc.py: -------------------------------------------------------------------------------- 1 | a = [[1, 2], [3, 4]] 2 | b = [[5, 1], [2, 1]] 3 | 4 | c = [[sum(i * j for i, j in zip(r, c)) for c in zip(*b)] 5 | for r in a] 6 | print(c) 7 | -------------------------------------------------------------------------------- /ch5/dictionary.comprehensions.duplicates.py: -------------------------------------------------------------------------------- 1 | word = 'Hello' 2 | 3 | swaps = {c: c.swapcase() for c in word} 4 | 5 | print(swaps) # prints: {'o': 'O', 'l': 'L', 'e': 'E', 'H': 'h'} 6 | -------------------------------------------------------------------------------- /ch5/dictionary.comprehensions.positions.py: -------------------------------------------------------------------------------- 1 | word = 'Hello' 2 | 3 | positions = {c: k for k, c in enumerate(word)} 4 | 5 | print(positions) # prints: {'l': 3, 'o': 4, 'e': 1, 'H': 0} 6 | -------------------------------------------------------------------------------- /ch5/sum.example.2.py: -------------------------------------------------------------------------------- 1 | s = sum([n**2 for n in range(10**8)]) # this is killed 2 | s = sum(n**2 for n in range(10**8)) # this succeeds 3 | 4 | print(s) # prints: 333333328333333350000000 5 | -------------------------------------------------------------------------------- /ch11/assertions.py: -------------------------------------------------------------------------------- 1 | mylist = [1, 2, 3] # this ideally comes from some place 2 | 3 | assert 4 == len(mylist) # this will break 4 | 5 | for position in range(4): 6 | print(mylist[position]) 7 | -------------------------------------------------------------------------------- /ch3/infinite.py: -------------------------------------------------------------------------------- 1 | from itertools import count 2 | 3 | for n in count(5, 3): 4 | if n > 20: 5 | break 6 | print(n, end=', ') # instead of newline, comma and space 7 | 8 | print() 9 | -------------------------------------------------------------------------------- /ch4/vat.function.py: -------------------------------------------------------------------------------- 1 | def calculate_price_with_vat(price, vat): 2 | return price * (100 + vat) / 100 3 | 4 | 5 | if __name__ == "__main__": 6 | print(calculate_price_with_vat(100, 20)) 7 | -------------------------------------------------------------------------------- /ch5/gen.map.py: -------------------------------------------------------------------------------- 1 | def adder(*n): 2 | return sum(n) 3 | 4 | s1 = sum(map(lambda n: adder(*n), zip(range(100), range(1, 101)))) 5 | s2 = sum(adder(*n) for n in zip(range(100), range(1, 101))) 6 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templates/records/messages.html: -------------------------------------------------------------------------------- 1 | {% if messages %} 2 | {% for message in messages %} 3 |

{{ message }}

4 | {% endfor %} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /ch4/arguments.default.py: -------------------------------------------------------------------------------- 1 | def func(a, b=4, c=88): 2 | print(a, b, c) 3 | 4 | func(1) # prints: 1 4 88 5 | func(b=5, a=7, c=9) # prints: 7 5 9 6 | func(42, c=9) # prints: 42 4 9 7 | -------------------------------------------------------------------------------- /ch3/multiple.sequences.enumerate.py: -------------------------------------------------------------------------------- 1 | people = ['Jonas', 'Julio', 'Mike', 'Mez'] 2 | ages = [25, 30, 31, 39] 3 | for position, person in enumerate(people): 4 | age = ages[position] 5 | print(person, age) 6 | -------------------------------------------------------------------------------- /ch4/recursive.factorial.py: -------------------------------------------------------------------------------- 1 | def factorial(n): 2 | if n in (0, 1): # base case 3 | return 1 4 | return factorial(n - 1) * n # recursive case 5 | 6 | 7 | print([factorial(n) for n in range(10)]) 8 | -------------------------------------------------------------------------------- /ch5/pairs.for.loop.py: -------------------------------------------------------------------------------- 1 | 2 | items = 'ABCDE' 3 | pairs = [] 4 | 5 | for a in range(len(items)): 6 | for b in range(a, len(items)): 7 | pairs.append((items[a], items[b])) 8 | 9 | print(pairs) 10 | -------------------------------------------------------------------------------- /ch5/set.comprehensions.py: -------------------------------------------------------------------------------- 1 | word = 'Hello' 2 | 3 | letters1 = set(c for c in word) 4 | letters2 = {c for c in word} 5 | print(letters1) # prints: {'l', 'o', 'H', 'e'} 6 | print(letters1 == letters2) # prints: True 7 | -------------------------------------------------------------------------------- /ch10/flask/templates/main.html: -------------------------------------------------------------------------------- 1 | 2 | Hello from Flask 3 |

4 | {% if name %} 5 | Hello {{ name }}! 6 | {% else %} 7 | Hello shy person! 8 | {% endif %} 9 |

10 | -------------------------------------------------------------------------------- /ch3/simple.for.2.py: -------------------------------------------------------------------------------- 1 | surnames = ['Rivest', 'Shamir', 'Adleman'] 2 | for position in range(len(surnames)): 3 | print(position, surnames[position]) 4 | # print(surnames[position][0], end='') # try swapping prints 5 | -------------------------------------------------------------------------------- /ch4/arguments.variable.keyword.py: -------------------------------------------------------------------------------- 1 | def func(**kwargs): 2 | print(kwargs) 3 | 4 | # All calls equivalent. They print: {'a': 1, 'b': 42} 5 | func(a=1, b=42) 6 | func(**{'a': 1, 'b': 42}) 7 | func(**dict(a=1, b=42)) 8 | -------------------------------------------------------------------------------- /ch4/return.none.py: -------------------------------------------------------------------------------- 1 | def func(): 2 | pass 3 | 4 | func() # the return of this call won't be collected. It's lost. 5 | a = func() # the return of this one instead is collected into `a` 6 | print(a) # prints: None 7 | -------------------------------------------------------------------------------- /ch3/multiple.sequences.py: -------------------------------------------------------------------------------- 1 | people = ['Jonas', 'Julio', 'Mike', 'Mez'] 2 | ages = [25, 30, 31, 39] 3 | for position in range(len(people)): 4 | person = people[position] 5 | age = ages[position] 6 | print(person, age) 7 | -------------------------------------------------------------------------------- /ch4/arguments.variable.positional.unpacking.py: -------------------------------------------------------------------------------- 1 | def func(*args): 2 | print(args) 3 | 4 | values = (1, 3, -7, 9) 5 | func(values) # equivalent to: func((1, 3, -7, 9)) 6 | func(*values) # equivalent to: func(1, 3, -7, 9) 7 | -------------------------------------------------------------------------------- /ch4/filter.regular.py: -------------------------------------------------------------------------------- 1 | def is_multiple_of_five(n): 2 | return not n % 5 3 | 4 | 5 | def get_multiples_of_five(n): 6 | return list(filter(is_multiple_of_five, range(n))) 7 | 8 | 9 | print(get_multiples_of_five(50)) 10 | -------------------------------------------------------------------------------- /ch7/exceptions/json.example.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | json_data = '{}' # try: json_data = 2, and json_data = '{{' 4 | 5 | try: 6 | data = json.loads(json_data) 7 | except (ValueError, TypeError) as e: 8 | print(type(e), e) 9 | -------------------------------------------------------------------------------- /ch1/scopes1.py: -------------------------------------------------------------------------------- 1 | # Local versus Global 2 | 3 | # we define a function, called local 4 | def local(): 5 | m = 7 6 | print(m) 7 | 8 | m = 5 9 | print(m) 10 | 11 | # we call, or `execute` the function local 12 | local() 13 | -------------------------------------------------------------------------------- /ch7/filter_funcs_triangulation.py: -------------------------------------------------------------------------------- 1 | def filter_ints(v): 2 | return [3, 5, 8] 3 | 4 | def is_positive(n): 5 | return n > 0 6 | 7 | 8 | if __name__ == "__main__": 9 | print(filter_ints([3, -4, 0, 5, 8])) # [3, 5, 8] 10 | -------------------------------------------------------------------------------- /requirements/requirements.in: -------------------------------------------------------------------------------- 1 | jupyter 2 | ipython 3 | pandas 4 | numpy 5 | matplotlib 6 | Faker 7 | pep8 8 | beautifulsoup4 9 | requests 10 | Delorean 11 | xlwt 12 | django==1.8.17 13 | falcon 14 | Flask 15 | gunicorn 16 | cryptography -------------------------------------------------------------------------------- /ch4/key.points.mutable.assignment.py: -------------------------------------------------------------------------------- 1 | x = [1, 2, 3] 2 | def func(x): 3 | x[1] = 42 # this changes the caller! 4 | x = 'something else' # this points x to a new string object 5 | 6 | func(x) 7 | print(x) # still prints: [1, 42, 3] 8 | -------------------------------------------------------------------------------- /requirements/requirements_test.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file requirements_test.txt requirements_test.in 6 | # 7 | nose-parameterized==0.5.0 8 | nose==1.3.7 9 | -------------------------------------------------------------------------------- /ch3/primes.else.py: -------------------------------------------------------------------------------- 1 | primes = [] 2 | upto = 100 3 | for n in range(2, upto + 1): 4 | for divisor in range(2, n): 5 | if n % divisor == 0: 6 | break 7 | else: 8 | primes.append(n) 9 | 10 | print(primes) 11 | -------------------------------------------------------------------------------- /ch4/return.single.value.py: -------------------------------------------------------------------------------- 1 | def factorial(n): 2 | if n in (0, 1): 3 | return 1 4 | result = n 5 | for k in range(2, n): 6 | result *= k 7 | return result 8 | 9 | f5 = factorial(5) # f5 = 120 10 | print(f5) 11 | -------------------------------------------------------------------------------- /ch5/maxims.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | >>> a = [5, 9, 2, 4, 7] 4 | >>> b = [3, 7, 1, 9, 2] 5 | >>> c = [6, 8, 0, 5, 3] 6 | >>> maxs = map(lambda n: max(*n), zip(a, b, c)) 7 | >>> list(maxs) 8 | [6, 9, 2, 9, 7] 9 | -------------------------------------------------------------------------------- /ch1/names.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | n = 3 # integer number 4 | address = "221b Baker Street, NW1 6XE, London" # Sherlock Holmes' address 5 | employee = { 6 | 'age': 45, 7 | 'role': 'CTO', 8 | 'SSN': 'AB1234567', 9 | } 10 | -------------------------------------------------------------------------------- /ch4/scoping.level.1.py: -------------------------------------------------------------------------------- 1 | def my_function(): 2 | test = 1 # this is defined in the local scope of the function 3 | print('my_function:', test) 4 | 5 | 6 | test = 0 # this is defined in the global scope 7 | my_function() 8 | print('global:', test) 9 | -------------------------------------------------------------------------------- /ch7/filter_funcs.py: -------------------------------------------------------------------------------- 1 | def filter_ints(v): 2 | return [num for num in v if is_positive(num)] 3 | 4 | def is_positive(n): 5 | return n > 0 6 | 7 | 8 | if __name__ == "__main__": 9 | print(filter_ints([3, -4, 0, 5, 8])) # [3, 5, 8] 10 | -------------------------------------------------------------------------------- /ch2/README.md: -------------------------------------------------------------------------------- 1 | Chapter 2 data files 2 | ==================== 3 | 4 | The files in this folder are not supposed to work if run. 5 | They serve as source for the book chapters, and to provide a 6 | quick copy/paste tool for whoever would need their content. 7 | -------------------------------------------------------------------------------- /ch5/gen.send.preparation.py: -------------------------------------------------------------------------------- 1 | def counter(start=0): 2 | n = start 3 | while True: 4 | yield n 5 | n += 1 6 | 7 | c = counter() 8 | print(next(c)) # prints: 0 9 | print(next(c)) # prints: 1 10 | print(next(c)) # prints: 2 11 | -------------------------------------------------------------------------------- /ch3/binary.2.py: -------------------------------------------------------------------------------- 1 | n = 39 2 | remainders = [] 3 | while n > 0: 4 | n, remainder = divmod(n, 2) 5 | remainders.append(remainder) 6 | 7 | # reassign the list to its reversed copy and print it 8 | remainders = remainders[::-1] 9 | print(remainders) 10 | -------------------------------------------------------------------------------- /ch3/multiple.sequences.while.py: -------------------------------------------------------------------------------- 1 | people = ['Jonas', 'Julio', 'Mike', 'Mez'] 2 | ages = [25, 30, 31, 39] 3 | position = 0 4 | while position < len(people): 5 | person = people[position] 6 | age = ages[position] 7 | print(person, age) 8 | position += 1 9 | -------------------------------------------------------------------------------- /ch10/regex/entries/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.forms import ModelForm 3 | 4 | from .models import Entry 5 | 6 | 7 | class EntryForm(ModelForm): 8 | class Meta: 9 | model = Entry 10 | fields = ['pattern', 'test_string'] 11 | -------------------------------------------------------------------------------- /ch5/dictionary.comprehensions.py: -------------------------------------------------------------------------------- 1 | from string import ascii_lowercase 2 | 3 | 4 | lettermap = dict((c, k) for k, c in enumerate(ascii_lowercase, 1)) 5 | # lettermap = {c: k for k, c in enumerate(ascii_lowercase, 1)} 6 | 7 | 8 | from pprint import pprint 9 | pprint(lettermap) -------------------------------------------------------------------------------- /ch4/arguments.all.py: -------------------------------------------------------------------------------- 1 | def func(a, b, c=7, *args, **kwargs): 2 | print('a, b, c:', a, b, c) 3 | print('args:', args) 4 | print('kwargs:', kwargs) 5 | 6 | func(1, 2, 3, *(5, 7, 9), **{'A': 'a', 'B': 'b'}) 7 | func(1, 2, 3, 5, 7, 9, A='a', B='b') # same as previous one 8 | -------------------------------------------------------------------------------- /ch11/custom.py: -------------------------------------------------------------------------------- 1 | def debug(*msg, print_separator=True): 2 | print(*msg) 3 | if print_separator: 4 | print('-' * 40) 5 | 6 | 7 | debug('Data is ...') 8 | debug('Different', 'Strings', 'Are not a problem') 9 | debug('After while loop', print_separator=False) 10 | -------------------------------------------------------------------------------- /ch3/multiple.sequences.explicit.py: -------------------------------------------------------------------------------- 1 | people = ['Jonas', 'Julio', 'Mike', 'Mez'] 2 | ages = [25, 30, 31, 39] 3 | nationalities = ['Belgium', 'Spain', 'England', 'Bangladesh'] 4 | for person, age, nationality in zip(people, ages, nationalities): 5 | print(person, age, nationality) 6 | -------------------------------------------------------------------------------- /ch4/arguments.defaults.mutable.py: -------------------------------------------------------------------------------- 1 | def func(a=[], b={}): 2 | print(a) 3 | print(b) 4 | print('#' * 12) 5 | a.append(len(a)) # this will affect a's default value 6 | b[len(a)] = len(a) # and this will affect b's one 7 | 8 | func() 9 | func() 10 | func() 11 | -------------------------------------------------------------------------------- /ch4/return.single.value.2.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | from operator import mul 3 | 4 | 5 | def factorial(n): 6 | return reduce(mul, range(1, n + 1), 1) 7 | 8 | 9 | f5 = factorial(5) # f5 = 120 10 | print(f5) 11 | print([factorial(k) for k in range(10)]) 12 | -------------------------------------------------------------------------------- /ch3/range.py: -------------------------------------------------------------------------------- 1 | >>> list(range(10)) # one value: from 0 to value (excluded) 2 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 3 | >>> list(range(3, 8)) # two values: from start to stop (excluded) 4 | [3, 4, 5, 6, 7] 5 | >>> list(range(-10, 10, 4)) # three values: step is added 6 | [-10, -6, -2, 2, 6] 7 | -------------------------------------------------------------------------------- /ch3/multiple.sequences.implicit.py: -------------------------------------------------------------------------------- 1 | people = ['Jonas', 'Julio', 'Mike', 'Mez'] 2 | ages = [25, 30, 31, 39] 3 | nationalities = ['Belgium', 'Spain', 'England', 'Bangladesh'] 4 | for data in zip(people, ages, nationalities): 5 | person, age, nationality = data 6 | print(person, age, nationality) 7 | -------------------------------------------------------------------------------- /ch4/scoping.level.2.py: -------------------------------------------------------------------------------- 1 | def outer(): 2 | test = 1 # outer scope 3 | 4 | def inner(): 5 | test = 2 # inner scope 6 | print('inner:', test) 7 | 8 | inner() 9 | print('outer:', test) 10 | 11 | 12 | test = 0 # global scope 13 | outer() 14 | print('global:', test) 15 | -------------------------------------------------------------------------------- /ch7/filter_funcs_refactored.py: -------------------------------------------------------------------------------- 1 | def filter_ints(v): 2 | v = [num for num in v if num != 0] # 1 3 | return [num for num in v if is_positive(num)] 4 | 5 | def is_positive(n): 6 | return n > 0 7 | 8 | 9 | if __name__ == "__main__": 10 | print(filter_ints([3, -4, 0, 5, 8])) # [3, 5, 8] 11 | -------------------------------------------------------------------------------- /ch10/regex/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "regex.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /ch12/pwdweb/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pwdweb.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /ch3/ternary.py: -------------------------------------------------------------------------------- 1 | order_total = 247 # GBP 2 | 3 | # classic if/else form 4 | if order_total > 100: 5 | discount = 25 # GBP 6 | else: 7 | discount = 0 # GBP 8 | print(order_total, discount) 9 | 10 | # ternary operator 11 | discount = 25 if order_total > 100 else 0 12 | print(order_total, discount) 13 | -------------------------------------------------------------------------------- /ch5/even.squares.py: -------------------------------------------------------------------------------- 1 | # using map and filter 2 | sq1 = list( 3 | filter(lambda n: not n % 2, map(lambda n: n ** 2, range(10))) 4 | ) 5 | 6 | # equivalent, but using list comprehensions 7 | sq2 = [n ** 2 for n in range(10) if not n % 2] 8 | 9 | print(sq1, sq1 == sq2) # prints: [0, 4, 16, 36, 64] True 10 | -------------------------------------------------------------------------------- /ch5/gen.send.preparation.stop.py: -------------------------------------------------------------------------------- 1 | stop = False 2 | 3 | def counter(start=0): 4 | n = start 5 | while not stop: 6 | yield n 7 | n += 1 8 | 9 | c = counter() 10 | print(next(c)) # prints: 0 11 | print(next(c)) # prints: 1 12 | stop = True 13 | print(next(c)) # raises StopIteration 14 | -------------------------------------------------------------------------------- /ch3/for.else.py: -------------------------------------------------------------------------------- 1 | class DriverException(Exception): 2 | pass 3 | 4 | 5 | people = [('James', 17), ('Kirk', 9), ('Lars', 13), ('Robert', 8)] 6 | for person, age in people: 7 | if age >= 18: 8 | driver = (person, age) 9 | break 10 | else: 11 | raise DriverException('Driver not found.') 12 | -------------------------------------------------------------------------------- /ch10/flask/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flask import Flask, render_template 3 | 4 | app = Flask(__name__) 5 | 6 | 7 | @app.route('/') 8 | @app.route('/') 9 | def hello(name=None): 10 | return render_template('main.html', name=name) 11 | 12 | 13 | if __name__ == '__main__': 14 | app.run() 15 | -------------------------------------------------------------------------------- /ch3/taxes.py: -------------------------------------------------------------------------------- 1 | income = 15000 2 | if income < 10000: 3 | tax_coefficient = 0.0 #1 4 | elif income < 30000: 5 | tax_coefficient = 0.2 #2 6 | elif income < 100000: 7 | tax_coefficient = 0.35 #3 8 | else: 9 | tax_coefficient = 0.45 #4 10 | 11 | print('I will pay:', income * tax_coefficient, 'in taxes') 12 | -------------------------------------------------------------------------------- /ch4/arguments.default.error.py: -------------------------------------------------------------------------------- 1 | def func(a, b=4, c=88): 2 | print(a, b, c) 3 | 4 | func(b=1, c=2, 42) # positional argument after keyword one 5 | 6 | """ 7 | File "arguments.default.error.py", line 4 8 | func(b=1, c=2, 42) 9 | ^ 10 | SyntaxError: non-keyword arg after keyword arg 11 | """ 12 | -------------------------------------------------------------------------------- /ch4/arguments.defaults.mutable.intermediate.call.py: -------------------------------------------------------------------------------- 1 | def func(a=[], b={}): 2 | print(a) 3 | print(b) 4 | print('#' * 12) 5 | a.append(len(a)) # this will affect a's default value 6 | b[len(a)] = len(a) # and this will affect b's one 7 | 8 | func() 9 | func(a=[1, 2, 3], b={'B': 1}) 10 | func() 11 | -------------------------------------------------------------------------------- /ch4/matrix.multiplication.func.py: -------------------------------------------------------------------------------- 1 | # this function could also be defined in another module 2 | def matrix_mul(a, b): 3 | return [[sum(i * j for i, j in zip(r, c)) for c in zip(*b)] 4 | for r in a] 5 | 6 | 7 | a = [[1, 2], [3, 4]] 8 | b = [[5, 1], [2, 1]] 9 | 10 | c = matrix_mul(a, b) 11 | print(c) 12 | -------------------------------------------------------------------------------- /ch7/exceptions/multiple.except.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | try: 4 | # some code 5 | except Exception1: 6 | # react to Exception1 7 | except (Exception2, Exception3): 8 | # react to Exception2 and Exception3 9 | except Exception3: 10 | # react to Exception3 11 | ... 12 | -------------------------------------------------------------------------------- /ch3/switch.py: -------------------------------------------------------------------------------- 1 | # this code is not supposed to be run 2 | if 1 <= day_number <= 5: 3 | day = 'Weekday' 4 | elif day_number == 6: 5 | day = 'Saturday' 6 | elif day_number == 0: 7 | day = 'Sunday' 8 | else: 9 | day = '' 10 | raise ValueError( 11 | str(day_number) + ' is not a valid day number.') 12 | -------------------------------------------------------------------------------- /ch4/no.side.effects.py: -------------------------------------------------------------------------------- 1 | # This file is not meant to be run 2 | >>> numbers = [4, 1, 7, 5] 3 | >>> sorted(numbers) # won't sort the original `numbers` list 4 | [1, 4, 5, 7] 5 | >>> numbers # let's verify 6 | [4, 1, 7, 5] # good, untouched 7 | >>> numbers.sort() # this will act on the list 8 | >>> numbers 9 | [1, 4, 5, 7] 10 | -------------------------------------------------------------------------------- /ch5/filter.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | >>> test = [2, 5, 8, 0, 0, 1, 0] 4 | >>> list(filter(None, test)) 5 | [2, 5, 8, 1] 6 | >>> list(filter(lambda x: x, test)) # equivalent to previous one 7 | [2, 5, 8, 1] 8 | >>> list(filter(lambda x: x > 4, test)) # keep only items > 4 9 | [5, 8] 10 | -------------------------------------------------------------------------------- /ch4/scoping.level.2.global.py: -------------------------------------------------------------------------------- 1 | def outer(): 2 | test = 1 # outer scope 3 | 4 | def inner(): 5 | global test 6 | test = 2 # global scope 7 | print('inner:', test) 8 | 9 | inner() 10 | print('outer:', test) 11 | 12 | 13 | test = 0 # global scope 14 | outer() 15 | print('global:', test) 16 | -------------------------------------------------------------------------------- /ch5/first.n.squares.py: -------------------------------------------------------------------------------- 1 | def get_squares(n): # classic function approach 2 | return [x ** 2 for x in range(n)] 3 | 4 | print(get_squares(10)) 5 | 6 | 7 | def get_squares_gen(n): # generator approach 8 | for x in range(n): 9 | yield x ** 2 # we yield, we don't return 10 | 11 | print(list(get_squares_gen(10))) 12 | -------------------------------------------------------------------------------- /ch11/traceback_validator.py: -------------------------------------------------------------------------------- 1 | class ValidatorError(Exception): 2 | """Raised when accessing a dict results in KeyError. """ 3 | 4 | d = {'some': 'key'} 5 | mandatory_key = 'some-other' 6 | try: 7 | print(d[mandatory_key]) 8 | except KeyError: 9 | raise ValidatorError( 10 | '`{}` not found in d.'.format(mandatory_key)) 11 | -------------------------------------------------------------------------------- /ch6/decorators/time.measure.start.py: -------------------------------------------------------------------------------- 1 | from time import sleep, time 2 | 3 | 4 | def f(): 5 | sleep(.3) 6 | 7 | def g(): 8 | sleep(.5) 9 | 10 | 11 | t = time() 12 | f() 13 | print('f took: ', time() - t) # f took: 0.3003859519958496 14 | 15 | t = time() 16 | g() 17 | print('g took:', time() - t) # g took: 0.5005719661712646 18 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.contrib import admin 3 | 4 | from .models import Record 5 | 6 | 7 | @admin.register(Record) 8 | class RecordAdmin(admin.ModelAdmin): 9 | list_display = ( 10 | 'title', 'username', 'email', 'url', 'created', 11 | 'last_modified', 'password' 12 | ) 13 | -------------------------------------------------------------------------------- /ch3/compress.py: -------------------------------------------------------------------------------- 1 | from itertools import compress 2 | data = range(10) 3 | even_selector = [1, 0] * 10 4 | odd_selector = [0, 1] * 10 5 | 6 | even_numbers = list(compress(data, even_selector)) 7 | odd_numbers = list(compress(data, odd_selector)) 8 | 9 | print(odd_selector) 10 | print(list(data)) 11 | print(even_numbers) 12 | print(odd_numbers) 13 | -------------------------------------------------------------------------------- /ch5/fibonacci.elegant.py: -------------------------------------------------------------------------------- 1 | def fibonacci(N): 2 | """Return all fibonacci numbers up to N. """ 3 | a, b = 0, 1 4 | while a <= N: 5 | yield a 6 | a, b = b, a + b 7 | 8 | 9 | print(list(fibonacci(0))) # [0] 10 | print(list(fibonacci(1))) # [0, 1, 1] 11 | print(list(fibonacci(50))) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 12 | -------------------------------------------------------------------------------- /ch4/scoping.level.2.nonlocal.py: -------------------------------------------------------------------------------- 1 | def outer(): 2 | test = 1 # outer scope 3 | 4 | def inner(): 5 | nonlocal test 6 | test = 2 # nearest enclosing scope 7 | print('inner:', test) 8 | 9 | inner() 10 | print('outer:', test) 11 | 12 | 13 | test = 0 # global scope 14 | outer() 15 | print('global:', test) 16 | -------------------------------------------------------------------------------- /ch5/gen.map.filter.py: -------------------------------------------------------------------------------- 1 | # finds the cubes of all multiples of 3 or 5 below N 2 | N = 20 3 | 4 | cubes1 = map( 5 | lambda n: (n, n**3), 6 | filter(lambda n: n % 3 == 0 or n % 5 == 0, range(N)) 7 | ) 8 | 9 | cubes2 = ( 10 | (n, n**3) for n in range(N) if n % 3 == 0 or n % 5 == 0) 11 | 12 | print(list(cubes1)) 13 | print(list(cubes2)) 14 | -------------------------------------------------------------------------------- /ch3/for.no.else.py: -------------------------------------------------------------------------------- 1 | class DriverException(Exception): 2 | pass 3 | 4 | 5 | people = [('James', 17), ('Kirk', 9), ('Lars', 13), ('Robert', 8)] 6 | driver = None 7 | for person, age in people: 8 | if age >= 18: 9 | driver = (person, age) 10 | break 11 | 12 | if driver is None: 13 | raise DriverException('Driver not found.') 14 | -------------------------------------------------------------------------------- /ch4/vat.py: -------------------------------------------------------------------------------- 1 | price = 100 # GBP, no VAT 2 | final_price1 = price * 1.2 3 | final_price2 = price + price / 5.0 4 | final_price3 = price * (100 + 20) / 100.0 5 | final_price4 = price + price * 0.2 6 | 7 | 8 | if __name__ == "__main__": 9 | for var, value in sorted(vars().items()): 10 | if 'price' in var: 11 | print(var, value) 12 | -------------------------------------------------------------------------------- /ch5/gen.yield.return.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | def geometric_progression(a, q): 4 | k = 0 5 | while True: 6 | result = a * q**k 7 | if result <= 100000: 8 | yield result 9 | else: 10 | return 11 | k += 1 12 | 13 | 14 | for n in geometric_progression(2, 5): 15 | print(n) 16 | -------------------------------------------------------------------------------- /ch6/decorators/time.measure.dry.py: -------------------------------------------------------------------------------- 1 | from time import sleep, time 2 | 3 | 4 | def f(): 5 | sleep(.3) 6 | 7 | def g(): 8 | sleep(.5) 9 | 10 | def measure(func): 11 | t = time() 12 | func() 13 | print(func.__name__, 'took:', time() - t) 14 | 15 | measure(f) # f took: 0.30041074752807617 16 | measure(g) # g took: 0.5006198883056641 17 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templatetags/record_extras.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django import template 3 | from django.utils.html import escape 4 | 5 | 6 | register = template.Library() 7 | 8 | 9 | @register.simple_tag 10 | def hide_password(password): 11 | return '{1}'.format( 12 | escape(password), '*' * len(password)) 13 | -------------------------------------------------------------------------------- /ch4/arguments.all.kwonly.py: -------------------------------------------------------------------------------- 1 | def func_with_kwonly(a, b=42, *args, c, d=256, **kwargs): 2 | print('a, b:', a, b) 3 | print('c, d:', c, d) 4 | print('args:', args) 5 | print('kwargs:', kwargs) 6 | 7 | # both calls equivalent 8 | func_with_kwonly(3, 42, c=0, d=1, *(7, 9, 11), e='E', f='F') 9 | func_with_kwonly(3, 42, *(7, 9, 11), c=0, d=1, e='E', f='F') 10 | -------------------------------------------------------------------------------- /ch6/oop/class.self.py: -------------------------------------------------------------------------------- 1 | class Square(): 2 | side = 8 3 | def area(self): # self is a reference to an instance 4 | return self.side ** 2 5 | 6 | sq = Square() 7 | print(sq.area()) # 64 (side is found on the class) 8 | print(Square.area(sq)) # 64 (equivalent to sq.area()) 9 | 10 | sq.side = 10 11 | print(sq.area()) # 100 (side is found on the instance) 12 | -------------------------------------------------------------------------------- /ch1/scopes2.py: -------------------------------------------------------------------------------- 1 | # Local versus Global 2 | 3 | def local(): 4 | # m doesn't belong to the scope defined by the local function 5 | # so Python will keep looking into the next enclosing scope. 6 | # m is finally found in the global scope 7 | print(m, 'printing from the local scope') 8 | 9 | m = 5 10 | print(m, 'printing from the global scope') 11 | 12 | local() 13 | -------------------------------------------------------------------------------- /ch6/oop/simplest.class.py: -------------------------------------------------------------------------------- 1 | class Simplest(): # when empty, the braces are optional 2 | pass 3 | 4 | print(type(Simplest)) # what type is this object? 5 | 6 | simp = Simplest() # we create an instance of Simplest: simp 7 | print(type(simp)) # what type is simp? 8 | # is simp an instance of Simplest? 9 | print(type(simp) == Simplest) # There's a better way for this 10 | -------------------------------------------------------------------------------- /ch5/scopes.py: -------------------------------------------------------------------------------- 1 | A = 100 2 | ex1 = [A for A in range(5)] 3 | print(A) # prints: 100 4 | 5 | ex2 = list(A for A in range(5)) 6 | print(A) # prints: 100 7 | 8 | ex3 = dict((A, 2 * A) for A in range(5)) 9 | print(A) # prints: 100 10 | 11 | ex4 = set(A for A in range(5)) 12 | print(A) # prints: 100 13 | 14 | s = 0 15 | for A in range(5): 16 | s += A 17 | print(A) # prints: 4 18 | -------------------------------------------------------------------------------- /ch6/oop/class.init.py: -------------------------------------------------------------------------------- 1 | class Rectangle(): 2 | def __init__(self, side_a, side_b): 3 | self.side_a = side_a 4 | self.side_b = side_b 5 | 6 | def area(self): 7 | return self.side_a * self.side_b 8 | 9 | r1 = Rectangle(10, 4) 10 | print(r1.side_a, r1.side_b) # 10 4 11 | print(r1.area()) # 40 12 | 13 | r2 = Rectangle(7, 3) 14 | print(r2.area()) # 21 15 | -------------------------------------------------------------------------------- /ch6/oop/class.price.py: -------------------------------------------------------------------------------- 1 | class Price(): 2 | def final_price(self, vat, discount=0): 3 | """Returns price after applying vat and fixed discount.""" 4 | return (self.net_price * (100 + vat) / 100) - discount 5 | 6 | p1 = Price() 7 | p1.net_price = 100 8 | print(Price.final_price(p1, 20, 10)) # 110 (100 * 1.2 - 10) 9 | print(p1.final_price(20, 10)) # equivalent 10 | -------------------------------------------------------------------------------- /ch4/arguments.variable.positional.py: -------------------------------------------------------------------------------- 1 | def minimum(*n): 2 | # print(n) # n is a tuple 3 | if n: # explained after the code 4 | mn = n[0] 5 | for value in n[1:]: 6 | if value < mn: 7 | mn = value 8 | print(mn) 9 | 10 | minimum(1, 3, -7, 9) # n = (1, 3, -7, 9) - prints: -7 11 | minimum() # n = () - prints: nothing 12 | -------------------------------------------------------------------------------- /ch5/gen.send.py: -------------------------------------------------------------------------------- 1 | def counter(start=0): 2 | n = start 3 | while True: 4 | result = yield n # A 5 | print(type(result), result) # B 6 | if result == 'Q': 7 | break 8 | n += 1 9 | 10 | c = counter() 11 | print(next(c)) # C 12 | print(c.send('Wow!')) # D 13 | print(next(c)) # E 14 | print(c.send('Q')) # F 15 | -------------------------------------------------------------------------------- /ch5/pythagorean.triple.comprehension.py: -------------------------------------------------------------------------------- 1 | from math import sqrt 2 | # this step is the same as before 3 | mx = 10 4 | legs = [(a, b, sqrt(a**2 + b**2)) 5 | for a in range(1, mx) for b in range(a, mx)] 6 | # here we combine filter and map in one CLEAN list comprehension 7 | legs = [(a, b, int(c)) for a, b, c in legs if c.is_integer()] 8 | 9 | print(legs) # prints: [(3, 4, 5), (6, 8, 10)] 10 | -------------------------------------------------------------------------------- /ch5/pythagorean.triple.py: -------------------------------------------------------------------------------- 1 | from math import sqrt 2 | 3 | # this will generate all possible pairs 4 | mx = 10 5 | legs = [(a, b, sqrt(a**2 + b**2)) 6 | for a in range(1, mx) for b in range(a, mx)] 7 | # this will filter out all non pythagorean triples 8 | legs = list( 9 | filter(lambda triple: triple[2].is_integer(), legs)) 10 | 11 | print(legs) # prints: [(3, 4, 5.0), (6, 8, 10.0)] 12 | -------------------------------------------------------------------------------- /ch5/fibonacci.first.py: -------------------------------------------------------------------------------- 1 | def fibonacci(N): 2 | """Return all fibonacci numbers up to N. """ 3 | result = [0] 4 | next_n = 1 5 | while next_n <= N: 6 | result.append(next_n) 7 | next_n = sum(result[-2:]) 8 | return result 9 | 10 | 11 | print(fibonacci(0)) # [0] 12 | print(fibonacci(1)) # [0, 1, 1] 13 | print(fibonacci(50)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 14 | -------------------------------------------------------------------------------- /ch6/decorators/time.measure.arguments.py: -------------------------------------------------------------------------------- 1 | from time import sleep, time 2 | 3 | 4 | def f(sleep_time=0.1): 5 | sleep(sleep_time) 6 | 7 | def measure(func, *args, **kwargs): 8 | t = time() 9 | func(*args, **kwargs) 10 | print(func.__name__, 'took:', time() - t) 11 | 12 | measure(f, sleep_time=0.3) # f took: 0.3004162311553955 13 | measure(f, 0.2) # f took: 0.20028162002563477 14 | -------------------------------------------------------------------------------- /ch11/ipdebugger.py: -------------------------------------------------------------------------------- 1 | # d comes from a JSON payload we don't control 2 | d = {'first': 'v1', 'second': 'v2', 'fourth': 'v4'} 3 | # keys also comes from a JSON payload we don't control 4 | keys = ('first', 'second', 'third', 'fourth') 5 | 6 | def do_something_with_value(value): 7 | print(value) 8 | 9 | for key in keys: 10 | do_something_with_value(d[key]) 11 | 12 | print('Validation done.') 13 | -------------------------------------------------------------------------------- /ch5/zip.grades.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | >>> grades = [18, 23, 30, 27, 15, 9, 22] 4 | >>> avgs = [22, 21, 29, 24, 18, 18, 24] 5 | >>> list(zip(avgs, grades)) 6 | [(22, 18), (21, 23), (29, 30), (24, 27), (18, 15), (18, 9), (24, 22)] 7 | >>> list(map(lambda *a: a, avgs, grades)) # equivalent to zip 8 | [(22, 18), (21, 23), (29, 30), (24, 27), (18, 15), (18, 9), (24, 22)] 9 | -------------------------------------------------------------------------------- /tests/test_ch7/test_filter_funcs_final.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from nose.tools import assert_list_equal 3 | 4 | from ch7.filter_funcs import filter_ints 5 | 6 | 7 | class FilterIntsTestCase(TestCase): 8 | 9 | def test_filter_ints_return_value(self): 10 | v = [3, -4, 0, -2, 5, 0, 8, -1] 11 | result = filter_ints(v) 12 | assert_list_equal([3, 5, 8], result) 13 | -------------------------------------------------------------------------------- /ch3/switch.js: -------------------------------------------------------------------------------- 1 | switch (day_number) { 2 | case 1: 3 | case 2: 4 | case 3: 5 | case 4: 6 | case 5: 7 | day = "Weekday"; 8 | break; 9 | case 6: 10 | day = "Saturday"; 11 | break; 12 | case 0: 13 | day = "Sunday"; 14 | break; 15 | default: 16 | day = ""; 17 | alert(day_number + ' is not a valid day number.') 18 | } 19 | -------------------------------------------------------------------------------- /ch5/fibonacci.second.py: -------------------------------------------------------------------------------- 1 | def fibonacci(N): 2 | """Return all fibonacci numbers up to N. """ 3 | yield 0 4 | if N == 0: 5 | return 6 | a = 0 7 | b = 1 8 | while b <= N: 9 | yield b 10 | a, b = b, a + b 11 | 12 | 13 | print(list(fibonacci(0))) # [0] 14 | print(list(fibonacci(1))) # [0, 1, 1] 15 | print(list(fibonacci(50))) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 16 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.forms import ModelForm, Textarea 3 | 4 | from .models import Record 5 | 6 | 7 | class RecordForm(ModelForm): 8 | class Meta: 9 | model = Record 10 | fields = ['title', 'username', 'email', 'url', 11 | 'password', 'notes'] 12 | widgets = {'notes': Textarea( 13 | attrs={'cols': 40, 'rows': 4})} 14 | -------------------------------------------------------------------------------- /ch3/any.py: -------------------------------------------------------------------------------- 1 | items = [0, None, 0.0, True, 0, 7] # True and 7 evaluate to True 2 | 3 | found = False # this is called "flag" 4 | for item in items: 5 | print('scanning item', item) 6 | if item: 7 | found = True # we update the flag 8 | break 9 | 10 | if found: # we inspect the flag 11 | print('At least one item evaluates to True') 12 | else: 13 | print('All items evaluate to False') 14 | -------------------------------------------------------------------------------- /ch3/primes.py: -------------------------------------------------------------------------------- 1 | primes = [] # this will contain the primes in the end 2 | upto = 100 # the limit, inclusive 3 | for n in range(2, upto + 1): 4 | is_prime = True # flag, new at each iteration of outer for 5 | for divisor in range(2, n): 6 | if n % divisor == 0: 7 | is_prime = False 8 | break 9 | if is_prime: # check on flag 10 | primes.append(n) 11 | 12 | print(primes) 13 | -------------------------------------------------------------------------------- /ch5/pythagorean.triple.int.py: -------------------------------------------------------------------------------- 1 | from math import sqrt 2 | 3 | mx = 10 4 | legs = [(a, b, sqrt(a**2 + b**2)) 5 | for a in range(1, mx) for b in range(a, mx)] 6 | legs = filter(lambda triple: triple[2].is_integer(), legs) 7 | 8 | # this will make the third number in the tuples integer 9 | legs = list( 10 | map(lambda triple: triple[:2] + (int(triple[2]), ), legs)) 11 | 12 | print(legs) # prints: [(3, 4, 5), (6, 8, 10)] 13 | -------------------------------------------------------------------------------- /ch6/oop/mro.py: -------------------------------------------------------------------------------- 1 | class A: 2 | label = 'a' 3 | 4 | class B(A): 5 | pass # was: label = 'b' 6 | 7 | class C(A): 8 | label = 'c' 9 | 10 | class D(B, C): 11 | pass 12 | 13 | 14 | d = D() 15 | print(d.label) # 'c' 16 | print(d.__class__.mro()) # notice another way to get the MRO 17 | # prints: 18 | # [, , 19 | # , , ] 20 | -------------------------------------------------------------------------------- /ch12/pwdapi/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import falcon 3 | 4 | from core.handlers import ( 5 | PasswordValidatorHandler, 6 | PasswordGeneratorHandler, 7 | ) 8 | 9 | 10 | validation_handler = PasswordValidatorHandler() 11 | generator_handler = PasswordGeneratorHandler() 12 | 13 | app = falcon.API() 14 | app.add_route('/password/validate/', validation_handler) 15 | app.add_route('/password/generate/', generator_handler) 16 | -------------------------------------------------------------------------------- /ch5/first.n.squares.manual.method.py: -------------------------------------------------------------------------------- 1 | def get_squares_gen(n): 2 | for x in range(n): 3 | yield x ** 2 4 | 5 | squares = get_squares_gen(3) 6 | print(squares.__next__()) # prints: 0 7 | print(squares.__next__()) # prints: 1 8 | print(squares.__next__()) # prints: 4 9 | # the following raises StopIteration, the generator is exhausted, 10 | # any further call to next will keep raising StopIteration 11 | print(squares.__next__()) 12 | -------------------------------------------------------------------------------- /ch6/oop/super.duplication.py: -------------------------------------------------------------------------------- 1 | class Book: 2 | 3 | def __init__(self, title, publisher, pages): 4 | self.title = title 5 | self.publisher = publisher 6 | self.pages = pages 7 | 8 | 9 | class Ebook(Book): 10 | 11 | def __init__(self, title, publisher, pages, format_): 12 | self.title = title 13 | self.publisher = publisher 14 | self.pages = pages 15 | self.format_ = format_ 16 | -------------------------------------------------------------------------------- /ch5/squares.map.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | # If you code like this you are not a Python guy! ;) 4 | >>> squares = [] 5 | >>> for n in range(10): 6 | ... squares.append(n ** 2) 7 | ... 8 | >>> list(squares) 9 | [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 10 | 11 | # This is better, one line, nice and readable 12 | >>> squares = map(lambda n: n**2, range(10)) 13 | >>> list(squares) 14 | [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 15 | -------------------------------------------------------------------------------- /ch10/falcon/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import random 4 | 5 | import falcon 6 | 7 | from quotes import quotes 8 | 9 | 10 | class QuoteResource: 11 | def on_get(self, req, resp): 12 | quote = { 13 | 'quote': random.choice(quotes), 14 | 'author': 'The Buddha' 15 | } 16 | resp.body = json.dumps(quote) 17 | 18 | 19 | api = falcon.API() 20 | api.add_route('/quote', QuoteResource()) 21 | -------------------------------------------------------------------------------- /ch6/oop/mro.simple.py: -------------------------------------------------------------------------------- 1 | class A: 2 | label = 'a' 3 | 4 | class B(A): 5 | label = 'b' 6 | 7 | class C(A): 8 | label = 'c' 9 | 10 | class D(B, C): 11 | pass 12 | 13 | d = D() 14 | print(d.label) # Hypothetically this could be either 'b' or 'c' 15 | print(d.__class__.mro()) # notice another way to get the MRO 16 | # prints: 17 | # [, , 18 | # , , ] 19 | -------------------------------------------------------------------------------- /ch6/oop/operator.overloading.py: -------------------------------------------------------------------------------- 1 | class Weird: 2 | def __init__(self, s): 3 | self._s = s 4 | 5 | def __len__(self): 6 | return len(self._s) 7 | 8 | def __bool__(self): 9 | return '42' in self._s 10 | 11 | 12 | weird = Weird('Hello! I am 9 years old!') 13 | print(len(weird)) # 24 14 | print(bool(weird)) # False 15 | 16 | weird2 = Weird('Hello! I am 42 years old!') 17 | print(len(weird2)) # 25 18 | print(bool(weird2)) # True 19 | -------------------------------------------------------------------------------- /ch10/regex/regex/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for regex project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "regex.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /ch12/pwdweb/pwdweb/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for pwdweb project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pwdweb.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /ch7/exceptions/try.syntax.py: -------------------------------------------------------------------------------- 1 | 2 | def try_syntax(numerator, denominator): 3 | try: 4 | print('In the try block: {}/{}' 5 | .format(numerator, denominator)) 6 | result = numerator / denominator 7 | except ZeroDivisionError as zde: 8 | print(zde) 9 | else: 10 | print('The result is:', result) 11 | return result 12 | finally: 13 | print('Exiting') 14 | 15 | print(try_syntax(12, 4)) 16 | print(try_syntax(11, 0)) 17 | -------------------------------------------------------------------------------- /ch10/regex/entries/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import models 3 | from django.contrib.auth.models import User 4 | from django.utils import timezone 5 | 6 | 7 | class Entry(models.Model): 8 | user = models.ForeignKey(User) 9 | pattern = models.CharField(max_length=255) 10 | test_string = models.CharField(max_length=255) 11 | date_added = models.DateTimeField(default=timezone.now) 12 | 13 | class Meta: 14 | verbose_name_plural = 'entries' 15 | -------------------------------------------------------------------------------- /ch5/performances.map.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | mx = 2 * 10 ** 7 4 | 5 | t = time() 6 | absloop = [] 7 | for n in range(mx): 8 | absloop.append(abs(n)) 9 | print('for loop: {:.4f} s'.format(time() - t)) 10 | 11 | t = time() 12 | abslist = [abs(n) for n in range(mx)] 13 | print('list comprehension: {:.4f} s'.format(time() - t)) 14 | 15 | t = time() 16 | absmap = list(map(abs, range(mx))) 17 | print('map: {:.4f} s'.format(time() - t)) 18 | 19 | print(absloop == abslist == absmap) 20 | -------------------------------------------------------------------------------- /ch6/oop/class.methods.factory.py: -------------------------------------------------------------------------------- 1 | class Point: 2 | 3 | def __init__(self, x, y): 4 | self.x = x 5 | self.y = y 6 | 7 | @classmethod 8 | def from_tuple(cls, coords): # cls is Point 9 | return cls(*coords) 10 | 11 | @classmethod 12 | def from_point(cls, point): # cls is Point 13 | return cls(point.x, point.y) 14 | 15 | 16 | p = Point.from_tuple((3, 7)) 17 | print(p.x, p.y) # 3 7 18 | q = Point.from_point(p) 19 | print(q.x, q.y) # 3 7 20 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templates/records/home.html: -------------------------------------------------------------------------------- 1 | {% extends "records/base.html" %} 2 | {% block title %}Welcome to the Records website.{% endblock %} 3 | 4 | {% block page-content %} 5 |

Welcome {{ user.first_name }}!

6 |
To create a record click 7 | here. 8 |
9 |
To see all records click 10 | here. 11 |
12 | {% endblock page-content %} 13 | -------------------------------------------------------------------------------- /ch4/arguments.variable.db.py: -------------------------------------------------------------------------------- 1 | def connect(**options): 2 | conn_params = { 3 | 'host': options.get('host', '127.0.0.1'), 4 | 'port': options.get('port', 5432), 5 | 'user': options.get('user', ''), 6 | 'pwd': options.get('pwd', ''), 7 | } 8 | print(conn_params) 9 | # we then connect to the db (commented out) 10 | # db.connect(**conn_params) 11 | 12 | connect() 13 | connect(host='127.0.0.42', port=5433) 14 | connect(port=5431, user='fab', pwd='gandalf') 15 | -------------------------------------------------------------------------------- /ch6/decorators/time.measure.deco1.py: -------------------------------------------------------------------------------- 1 | from time import sleep, time 2 | 3 | def f(sleep_time=0.1): 4 | sleep(sleep_time) 5 | 6 | def measure(func): 7 | def wrapper(*args, **kwargs): 8 | t = time() 9 | func(*args, **kwargs) 10 | print(func.__name__, 'took:', time() - t) 11 | return wrapper 12 | 13 | f = measure(f) # decoration point 14 | 15 | f(0.2) # f took: 0.2002875804901123 16 | f(sleep_time=0.3) # f took: 0.3003721237182617 17 | print(f.__name__) # wrapper <- ouch! 18 | -------------------------------------------------------------------------------- /ch4/func.attributes.py: -------------------------------------------------------------------------------- 1 | def multiplication(a, b=1): 2 | """Return a multiplied by b. """ 3 | return a * b 4 | 5 | 6 | if __name__ == "__main__": 7 | 8 | special_attributes = [ 9 | "__doc__", "__name__", "__qualname__", "__module__", 10 | "__defaults__", "__code__", "__globals__", "__dict__", 11 | "__closure__", "__annotations__", "__kwdefaults__", 12 | ] 13 | 14 | for attribute in special_attributes: 15 | print(attribute, '->', getattr(multiplication, attribute)) 16 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templates/records/record_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "records/base.html" %} 2 | {% block title %}Delete record{% endblock title %} 3 | 4 | {% block page-content %} 5 |

Confirm Record Deletion

6 |
{% csrf_token %} 7 |

Are you sure you want to delete "{{ object }}"?

8 |   9 | 10 | » cancel 11 |
12 | {% endblock page-content %} 13 | -------------------------------------------------------------------------------- /ch8/simple_server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cool Owls! 4 | 5 |

Welcome to my owl gallery

6 |
7 | 8 | 9 | 10 | 11 | 12 |
13 |

Do you like my owls?

14 | 15 | 16 | -------------------------------------------------------------------------------- /ch10/regex/entries/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.contrib import admin 3 | 4 | from .models import Entry 5 | 6 | 7 | @admin.register(Entry) 8 | class EntryAdmin(admin.ModelAdmin): 9 | fieldsets = [ 10 | ('Regular Expression', 11 | {'fields': ['pattern', 'test_string']}), 12 | ('Other Information', 13 | {'fields': ['user', 'date_added']}), 14 | ] 15 | list_display = ('pattern', 'test_string', 'user') 16 | list_filter = ['user'] 17 | search_fields = ['test_string'] 18 | -------------------------------------------------------------------------------- /tests/test_ch7/test_filter_funcs_final_triangulation.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, expectedFailure 2 | from nose.tools import assert_list_equal 3 | 4 | from ch7.filter_funcs_triangulation import filter_ints 5 | 6 | 7 | class FilterIntsTestCase(TestCase): 8 | 9 | @expectedFailure 10 | def test_filter_ints_return_value(self): 11 | v1 = [3, -4, 0, -2, 5, 0, 8, -1] 12 | v2 = [7, -3, 0, 0, 9, 1] 13 | 14 | assert_list_equal([3, 5, 8], filter_ints(v1)) 15 | assert_list_equal([7, 9, 1], filter_ints(v2)) 16 | -------------------------------------------------------------------------------- /ch1/scopes3.py: -------------------------------------------------------------------------------- 1 | # Local, Enclosing and Global 2 | 3 | 4 | def enclosing_func(): 5 | m = 13 6 | 7 | def local(): 8 | # m doesn't belong to the scope defined by the local 9 | # function so Python will keep looking into the next 10 | # enclosing scope. This time m is found in the enclosing 11 | # scope 12 | print(m, 'printing from the local scope') 13 | 14 | # calling the function local 15 | local() 16 | 17 | m = 5 18 | print(m, 'printing from the global scope') 19 | 20 | enclosing_func() 21 | -------------------------------------------------------------------------------- /ch11/custom_timestamp.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | 4 | def debug(*msg, timestamp=[None]): 5 | print(*msg) 6 | from time import time # local import 7 | if timestamp[0] is None: 8 | timestamp[0] = time() #1 9 | else: 10 | now = time() 11 | print(' Time elapsed: {:.3f}s'.format( 12 | now - timestamp[0])) 13 | timestamp[0] = now #2 14 | 15 | 16 | debug('Entering nasty piece of code...') 17 | sleep(.3) 18 | debug('First step done.') 19 | sleep(.5) 20 | debug('Second step done.') 21 | -------------------------------------------------------------------------------- /ch3/binary.py: -------------------------------------------------------------------------------- 1 | """ 2 | 6 / 2 = 3 (remainder: 0) 3 | 3 / 2 = 1 (remainder: 1) 4 | 1 / 2 = 0 (remainder: 1) 5 | List of remainders: 0, 1, 1. 6 | Inverse is 1, 1, 0, which is also the binary repres. of 6: 110 7 | """ 8 | 9 | n = 39 10 | remainders = [] 11 | while n > 0: 12 | remainder = n % 2 # remainder of division by 2 13 | remainders.append(remainder) # we keep track of remainders 14 | n //= 2 # we divide n by 2 15 | 16 | # reassign the list to its reversed copy and print it 17 | remainders = remainders[::-1] 18 | print(remainders) 19 | -------------------------------------------------------------------------------- /ch5/first.n.squares.manual.py: -------------------------------------------------------------------------------- 1 | def get_squares_gen(n): 2 | for x in range(n): 3 | yield x ** 2 4 | 5 | squares = get_squares_gen(4) # this creates a generator object 6 | print(squares) # 7 | print(next(squares)) # prints: 0 8 | print(next(squares)) # prints: 1 9 | print(next(squares)) # prints: 4 10 | print(next(squares)) # prints: 9 11 | # the following raises StopIteration, the generator is exhausted, 12 | # any further call to next will keep raising StopIteration 13 | print(next(squares)) 14 | -------------------------------------------------------------------------------- /ch2/bytearray.py: -------------------------------------------------------------------------------- 1 | # bytearray.py 2 | 3 | 4 | >>> bytearray() # empty bytearray object 5 | bytearray(b'') 6 | >>> bytearray(10) # zero-filled instance with given length 7 | bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 8 | >>> bytearray(range(5)) # bytearray from iterable of integers 9 | bytearray(b'\x00\x01\x02\x03\x04') 10 | >>> name = bytearray(b'Lina') #A - bytearray from bytes 11 | >>> name.replace(b'L', b'l') 12 | bytearray(b'lina') 13 | >>> name.endswith(b'na') 14 | True 15 | >>> name.upper() 16 | bytearray(b'LINA') 17 | >>> name.count(b'L') 18 | 1 19 | -------------------------------------------------------------------------------- /ch5/pythagorean.triple.generation.py: -------------------------------------------------------------------------------- 1 | from functions import gcd 2 | 3 | 4 | N = 50 5 | 6 | triples = sorted( # 1 7 | ((a, b, c) for a, b, c in ( # 2 8 | ((m**2 - n**2), (2 * m * n), (m**2 + n**2)) # 3 9 | for m in range(1, int(N**.5) + 1) # 4 10 | for n in range(1, m) # 5 11 | if (m - n) % 2 and gcd(m, n) == 1 # 6 12 | ) if c <= N), key=lambda *triple: sum(*triple) # 7 13 | ) 14 | 15 | 16 | print(triples) 17 | -------------------------------------------------------------------------------- /ch2/objects.py: -------------------------------------------------------------------------------- 1 | # objects.py 2 | 3 | # code block # 1 4 | >>> age = 42 5 | >>> age 6 | 42 7 | >>> age = 43 #A 8 | >>> age 9 | 43 10 | 11 | 12 | # code block # 2 13 | >>> age = 42 14 | >>> id(age) 15 | 10456352 16 | >>> age = 43 17 | >>> id(age) 18 | 10456384 19 | 20 | 21 | # code block # 3 22 | >>> class Person(): 23 | ... def __init__(self, age): 24 | ... self.age = age 25 | ... 26 | >>> fab = Person(age=39) 27 | >>> fab.age 28 | 39 29 | >>> id(fab) 30 | 139632387887456 31 | >>> fab.age = 29 # I wish! 32 | >>> id(fab) 33 | 139632387887456 # still the same id 34 | -------------------------------------------------------------------------------- /ch4/arguments.keyword.only.py: -------------------------------------------------------------------------------- 1 | def kwo(*a, c): 2 | print(a, c) 3 | 4 | kwo(1, 2, 3, c=7) # prints: (1, 2, 3) 7 5 | kwo(c=4) # prints: () 4 6 | # kwo(1, 2) # breaks, invalid syntax, with the following error 7 | # TypeError: kwo() missing 1 required keyword-only argument: 'c' 8 | 9 | def kwo2(a, b=42, *, c): 10 | print(a, b, c) 11 | 12 | kwo2(3, b=7, c=99) # prints: 3 7 99 13 | kwo2(3, c=13) # prints: 3 42 13 14 | # kwo2(3, 23) # breaks, invalid syntax, with the following error 15 | # TypeError: kwo2() missing 1 required keyword-only argument: 'c' 16 | -------------------------------------------------------------------------------- /ch6/oop/private.attrs.py: -------------------------------------------------------------------------------- 1 | class A: 2 | def __init__(self, factor): 3 | self._factor = factor 4 | 5 | def op1(self): 6 | print('Op1 with factor {}...'.format(self._factor)) 7 | 8 | class B(A): 9 | def op2(self, factor): 10 | self._factor = factor 11 | print('Op2 with factor {}...'.format(self._factor)) 12 | 13 | 14 | obj = B(100) 15 | obj.op1() # Op1 with factor 100... 16 | obj.op2(42) # Op2 with factor 42... 17 | obj.op1() # Op1 with factor 42... <- This is BAD 18 | 19 | print(obj.__dict__.keys()) 20 | # dict_keys(['_factor']) 21 | -------------------------------------------------------------------------------- /ch6/decorators/time.measure.deco2.py: -------------------------------------------------------------------------------- 1 | from time import sleep, time 2 | from functools import wraps 3 | 4 | 5 | def measure(func): 6 | @wraps(func) 7 | def wrapper(*args, **kwargs): 8 | t = time() 9 | func(*args, **kwargs) 10 | print(func.__name__, 'took:', time() - t) 11 | return wrapper 12 | 13 | 14 | @measure 15 | def f(sleep_time=0.1): 16 | """I'm a cat. I love to sleep! """ 17 | sleep(sleep_time) 18 | 19 | 20 | f(sleep_time=0.3) # f took: 0.30039525032043457 21 | print(f.__name__, ':', f.__doc__) 22 | # f : I'm a cat. I love to sleep! 23 | -------------------------------------------------------------------------------- /ch5/generator.expressions.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | >>> cubes = [k**3 for k in range(10)] # regular list 4 | >>> cubes 5 | [0, 1, 8, 27, 64, 125, 216, 343, 512, 729] 6 | >>> type(cubes) 7 | 8 | >>> cubes_gen = (k**3 for k in range(10)) # create as generator 9 | >>> cubes_gen 10 | at 0x7ff26b5db990> 11 | >>> type(cubes_gen) 12 | 13 | >>> list(cubes_gen) # this will exhaust the generator 14 | [0, 1, 8, 27, 64, 125, 216, 343, 512, 729] 15 | >>> list(cubes_gen) # nothing more to give 16 | [] 17 | -------------------------------------------------------------------------------- /ch4/data.science.example.py: -------------------------------------------------------------------------------- 1 | def do_report(data_source): 2 | # fetch and prepare data 3 | data = fetch_data(data_source) 4 | parsed_data = parse_data(data) 5 | filtered_data = filter_data(parsed_data) 6 | polished_data = polish_data(filtered_data) 7 | 8 | # run algorithms on data 9 | final_data = analyse(polished_data) 10 | 11 | # create and return report 12 | report = Report(final_data) 13 | return report 14 | 15 | 16 | if __name__ == "__main__": 17 | 18 | print( 19 | "Please don't call the `do_report` function. " 20 | "It's just and example." 21 | ) 22 | -------------------------------------------------------------------------------- /ch6/oop/class.attribute.shadowing.py: -------------------------------------------------------------------------------- 1 | class Point(): 2 | x = 10 3 | y = 7 4 | 5 | p = Point() 6 | print(p.x) # 10 (from class attribute) 7 | print(p.y) # 7 (from class attribute) 8 | 9 | p.x = 12 # p gets its own `x` attribute 10 | print(p.x) # 12 (now found on the instance) 11 | print(Point.x) # 10 (class attribute still the same) 12 | 13 | del p.x # we delete instance attribute 14 | print(p.x) # 10 (now search has to go again to find class attr) 15 | 16 | p.z = 3 # let's make it a 3D point 17 | print(p.z) # 3 18 | 19 | print(Point.z) 20 | # AttributeError: type object 'Point' has no attribute 'z' 21 | -------------------------------------------------------------------------------- /ch6/oop/private.attrs.fixed.py: -------------------------------------------------------------------------------- 1 | class A: 2 | def __init__(self, factor): 3 | self.__factor = factor 4 | 5 | def op1(self): 6 | print('Op1 with factor {}...'.format(self.__factor)) 7 | 8 | class B(A): 9 | def op2(self, factor): 10 | self.__factor = factor 11 | print('Op2 with factor {}...'.format(self.__factor)) 12 | 13 | 14 | obj = B(100) 15 | obj.op1() # Op1 with factor 100... 16 | obj.op2(42) # Op2 with factor 42... 17 | obj.op1() # Op1 with factor 100... <- Wohoo! Now it's GOOD! 18 | 19 | print(obj.__dict__.keys()) 20 | # dict_keys(['_A__factor', '_B__factor']) 21 | -------------------------------------------------------------------------------- /ch6/oop/super.explicit.py: -------------------------------------------------------------------------------- 1 | class Book: 2 | 3 | def __init__(self, title, publisher, pages): 4 | self.title = title 5 | self.publisher = publisher 6 | self.pages = pages 7 | 8 | 9 | class Ebook(Book): 10 | 11 | def __init__(self, title, publisher, pages, format_): 12 | Book.__init__(self, title, publisher, pages) 13 | self.format_ = format_ 14 | 15 | 16 | ebook = Ebook('Learning Python', 'Packt Publishing', 360, 'PDF') 17 | print(ebook.title) # Learning Python 18 | print(ebook.publisher) # Packt Publishing 19 | print(ebook.pages) # 360 20 | print(ebook.format_) # PDF 21 | -------------------------------------------------------------------------------- /ch4/lambda.explained.py: -------------------------------------------------------------------------------- 1 | """ 2 | myfunc = lambda [parameter_list]: expression 3 | 4 | def myfunc([parameter_list]): 5 | return expression 6 | """ 7 | 8 | # example 1: adder 9 | def adder(a, b): 10 | return a + b 11 | 12 | # is equivalent to: 13 | adder_lambda = lambda a, b: a + b 14 | 15 | # example 2: to uppercase 16 | def to_upper(s): 17 | return s.upper() 18 | 19 | # is equivalent to: 20 | to_upper_lambda = lambda s: s.upper() 21 | 22 | 23 | if __name__ == "__main__": 24 | 25 | print(adder(3, 4)) 26 | print(adder_lambda(3, 4)) 27 | 28 | print(to_upper("Hello")) 29 | print(to_upper_lambda("Hello")) 30 | -------------------------------------------------------------------------------- /ch3/discount.py: -------------------------------------------------------------------------------- 1 | from datetime import date, timedelta 2 | 3 | today = date.today() 4 | tomorrow = today + timedelta(days=1) # today + 1 day is tomorrow 5 | products = [ 6 | {'sku': '1', 'expiration_date': today, 'price': 100.0}, 7 | {'sku': '2', 'expiration_date': tomorrow, 'price': 50}, 8 | {'sku': '3', 'expiration_date': today, 'price': 20}, 9 | ] 10 | 11 | for product in products: 12 | if product['expiration_date'] != today: 13 | continue 14 | product['price'] *= 0.8 # equivalent to applying 20% discount 15 | print( 16 | 'Price for sku', product['sku'], 17 | 'is now', product['price']) 18 | -------------------------------------------------------------------------------- /ch4/return.multiple.py: -------------------------------------------------------------------------------- 1 | def moddiv(a, b): 2 | return a // b, a % b 3 | 4 | 5 | def test(n=10**4): 6 | from random import choice, random, randint 7 | m = 10 ** 6 8 | for x in range(n): 9 | a = choice((randint(0, m), m * random())) 10 | b = 0 11 | while not b: 12 | b = choice((randint(1, m), m * random())) 13 | 14 | r = divmod(a, b) 15 | r2 = moddiv(a, b) 16 | if r != r2: 17 | print('Difference: ', a, b, r, r2) 18 | 19 | 20 | if __name__ == "__main__": 21 | 22 | test(10 ** 6) 23 | print('Done') 24 | 25 | print(moddiv(20, 7)) # prints (2, 6) 26 | -------------------------------------------------------------------------------- /ch2/defaultdict.py: -------------------------------------------------------------------------------- 1 | # defaultdict.py 2 | 3 | 4 | >>> d = {} 5 | >>> d['age'] = d.get('age', 0) + 1 # age not there, we get 0 + 1 6 | >>> d 7 | {'age': 1} 8 | >>> d = {'age': 39} 9 | >>> d['age'] = d.get('age', 0) + 1 # age is there, we get 40 10 | >>> d 11 | {'age': 40} 12 | 13 | 14 | >>> from collections import defaultdict 15 | >>> dd = defaultdict(int) # int is the default type (0 the value) 16 | >>> dd['age'] += 1 # short for dd['age'] = dd['age'] + 1 17 | >>> dd 18 | defaultdict(, {'age': 1}) # 1, as expected 19 | >>> dd['age'] = 39 20 | >>> dd['age'] += 1 21 | >>> dd 22 | defaultdict(, {'age': 40}) # 40, as expected 23 | -------------------------------------------------------------------------------- /ch5/pythagorean.triple.generation.for.py: -------------------------------------------------------------------------------- 1 | from functions import gcd 2 | 3 | 4 | def gen_triples(N): 5 | for m in range(1, int(N**.5) + 1): # 1 6 | for n in range(1, m): # 2 7 | if (m - n) % 2 and gcd(m, n) == 1: # 3 8 | c = m**2 + n**2 # 4 9 | if c <= N: # 5 10 | a = m**2 - n**2 # 6 11 | b = 2 * m * n # 7 12 | yield (a, b, c) # 8 13 | 14 | 15 | triples = sorted( 16 | gen_triples(50), key=lambda *triple: sum(*triple)) # 9 17 | print(triples) 18 | -------------------------------------------------------------------------------- /ch7/exceptions/for.loop.py: -------------------------------------------------------------------------------- 1 | n = 100 2 | found = False 3 | for a in range(n): 4 | if found: break 5 | for b in range(n): 6 | if found: break 7 | for c in range(n): 8 | if 42 * a + 17 * b + c == 5096: 9 | found = True 10 | print(a, b, c) # 79 99 95 11 | 12 | 13 | class ExitLoopException(Exception): 14 | pass 15 | 16 | try: 17 | n = 100 18 | for a in range(n): 19 | for b in range(n): 20 | for c in range(n): 21 | if 42 * a + 17 * b + c == 5096: 22 | raise ExitLoopException(a, b, c) 23 | except ExitLoopException as ele: 24 | print(ele) # (79, 99, 95) 25 | -------------------------------------------------------------------------------- /ch2/tuples.py: -------------------------------------------------------------------------------- 1 | # tuples.py 2 | 3 | 4 | >>> t = () # empty tuple 5 | >>> type(t) 6 | 7 | >>> one_element_tuple = (42, ) # you need the comma! 8 | >>> three_elements_tuple = (1, 3, 5) # braces are optional here 9 | >>> a, b, c = 1, 2, 3 # tuple for multiple assignment 10 | >>> a, b, c # implicit tuple to print with one instruction 11 | (1, 2, 3) 12 | >>> 3 in three_elements_tuple # membership test 13 | True 14 | 15 | 16 | # swap 17 | >>> a, b = 1, 2 18 | >>> c = a # we need three lines and a temporary var c 19 | >>> a = b 20 | >>> b = c 21 | >>> a, b # a and b have been swapped 22 | (2, 1) 23 | >>> a, b = b, a # this is the Pythonic way to do it 24 | >>> a, b 25 | (1, 2) 26 | -------------------------------------------------------------------------------- /ch10/regex/entries/static/entries/css/main.css: -------------------------------------------------------------------------------- 1 | /* HTML tags */ 2 | a { color: #333; } 3 | /* Classes */ 4 | .footer { margin-top: 1em; color: #333; } 5 | .entries-table { border-spacing: 0; padding-top: 1em; } 6 | .entries-table thead { background-color: #333; color: #fff; 7 | padding: 2em; font-size: 1.4em; } 8 | .entries-table td { padding: 1em; vertical-align: top; } 9 | .light-gray { background-color: #eee; } 10 | .white { background-color: #fff; } 11 | .code { font-size: 1.1em; font-weight: bolder; color: #333; } 12 | .home-option { padding: .6em 0; } 13 | /* Messages */ 14 | .success, .errorlist {font-size: 1.2em; font-weight: bold; } 15 | .success {color: #25B725; } 16 | .errorlist {color: #B12B2B; } 17 | -------------------------------------------------------------------------------- /tests/test_ch7/test_filter_funcs_is_positive_correct.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from nose.tools import assert_equal, assert_list_equal 3 | 4 | from ch7.filter_funcs import ( 5 | filter_ints, is_positive 6 | ) 7 | 8 | 9 | class FilterIntsTestCase(TestCase): 10 | 11 | def test_filter_ints_return_value(self): 12 | v1 = [3, -4, 0, -2, 5, 0, 8, -1] 13 | v2 = [7, -3, 0, 0, 9, 1] 14 | 15 | assert_list_equal([3, 5, 8], filter_ints(v1)) 16 | assert_list_equal([7, 9, 1], filter_ints(v2)) 17 | 18 | def test_is_positive(self): 19 | assert_equal(False, is_positive(-1)) 20 | assert_equal(False, is_positive(0)) 21 | assert_equal(True, is_positive(1)) 22 | -------------------------------------------------------------------------------- /ch4/docstrings.py: -------------------------------------------------------------------------------- 1 | def square(n): 2 | """Return the square of a number n. """ 3 | return n ** 2 4 | 5 | def get_username(userid): 6 | """Return the username of a user given their id. """ 7 | return db.get(user_id=userid).username 8 | 9 | 10 | def connect(host, port, user, password): 11 | """Connect to a database. 12 | 13 | Connect to a PostgreSQL database directly, using the given 14 | parameters. 15 | 16 | :param host: The host IP. 17 | :param port: The desired port. 18 | :param user: The connection username. 19 | :param password: The connection password. 20 | :return: The connection object. 21 | """ 22 | # body of the function here... 23 | return connection 24 | -------------------------------------------------------------------------------- /ch10/regex/entries/templates/entries/insert.html: -------------------------------------------------------------------------------- 1 | {% extends "entries/base.html" %} 2 | {% block title%}Insert a new Entry{% endblock title %} 3 | 4 | {% block page-content %} 5 | {% if messages %} 6 | {% for message in messages %} 7 |

{{ message }}

8 | {% endfor %} 9 | {% endif %} 10 | 11 |

Insert a new Entry

12 |
13 | {% csrf_token %}{{ form.as_p }} 14 | 15 |

16 | {% endblock page-content %} 17 | 18 | {% block footer %} 19 | 20 | {% include "entries/footer.html" %} 21 | {% endblock footer %} 22 | -------------------------------------------------------------------------------- /ch6/oop/class.namespaces.py: -------------------------------------------------------------------------------- 1 | class Person(): 2 | species = 'Human' 3 | 4 | 5 | print(Person.species) # Human 6 | Person.alive = True # Added dynamically! 7 | print(Person.alive) # True 8 | 9 | man = Person() 10 | print(man.species) # Human (inherited) 11 | print(man.alive) # True (inherited) 12 | 13 | Person.alive = False 14 | print(man.alive) # False (inherited) 15 | 16 | man.name = 'Darth' 17 | man.surname = 'Vader' 18 | print(man.name, man.surname) # Darth Vader 19 | 20 | print(Person.name) 21 | # This doesn't work. We try to access an instance attribute 22 | # from a class. Doing the opposite works, but this will give 23 | # the following error: 24 | # AttributeError: type object 'Person' has no attribute 'name' 25 | -------------------------------------------------------------------------------- /ch6/oop/super.implicit.py: -------------------------------------------------------------------------------- 1 | class Book: 2 | 3 | def __init__(self, title, publisher, pages): 4 | self.title = title 5 | self.publisher = publisher 6 | self.pages = pages 7 | 8 | 9 | class Ebook(Book): 10 | 11 | def __init__(self, title, publisher, pages, format_): 12 | super().__init__(title, publisher, pages) 13 | # Another way to do the same thing is: 14 | # super(Ebook, self).__init__(title, publisher, pages) 15 | self.format_ = format_ 16 | 17 | 18 | ebook = Ebook('Learning Python', 'Packt Publishing', 360, 'PDF') 19 | print(ebook.title) # Learning Python 20 | print(ebook.publisher) # Packt Publishing 21 | print(ebook.pages) # 360 22 | print(ebook.format_) # PDF 23 | -------------------------------------------------------------------------------- /ch1/bike.py: -------------------------------------------------------------------------------- 1 | # bike.py 2 | 3 | # let's define the class Bike 4 | class Bike: 5 | 6 | def __init__(self, colour, frame_material): 7 | self.colour = colour 8 | self.frame_material = frame_material 9 | 10 | def brake(self): 11 | print("Braking!") 12 | 13 | # let's create a couple of instances 14 | red_bike = Bike('Red', 'Carbon fiber') 15 | blue_bike = Bike('Blue', 'Steel') 16 | 17 | # let's inspect the objects we have, instances of the Bike class. 18 | print(red_bike.colour) # prints: Red 19 | print(red_bike.frame_material) # prints: Carbon fiber 20 | print(blue_bike.colour) # prints: Blue 21 | print(blue_bike.frame_material) # prints: Steel 22 | 23 | # let's brake! 24 | red_bike.brake() # prints: Braking! 25 | -------------------------------------------------------------------------------- /ch6/decorators/decorators.factory.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | def max_result(threshold): 4 | def decorator(func): 5 | @wraps(func) 6 | def wrapper(*args, **kwargs): 7 | result = func(*args, **kwargs) 8 | if result > threshold: 9 | print( 10 | 'Result is too big ({0}). Max allowed is {1}.' 11 | .format(result, threshold)) 12 | return result 13 | return wrapper 14 | return decorator 15 | 16 | @max_result(75) 17 | def cube(n): 18 | return n ** 3 19 | 20 | @max_result(100) 21 | def square(n): 22 | return n ** 2 23 | 24 | @max_result(1000) 25 | def multiply(a, b): 26 | return a * b 27 | 28 | print(cube(5)) 29 | -------------------------------------------------------------------------------- /tests/test_ch7/test_filter_funcs_is_positive_better.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from nose.tools import assert_equal, assert_list_equal 3 | 4 | from ch7.filter_funcs import ( 5 | filter_ints, is_positive 6 | ) 7 | 8 | 9 | class FilterIntsTestCase(TestCase): 10 | 11 | def test_filter_ints_return_value(self): 12 | v1 = [3, -4, 0, -2, 5, 0, 8, -1] 13 | v2 = [7, -3, 0, 0, 9, 1] 14 | 15 | assert_list_equal([3, 5, 8], filter_ints(v1)) 16 | assert_list_equal([7, 9, 1], filter_ints(v2)) 17 | 18 | def test_is_positive(self): 19 | assert_equal(False, is_positive(0)) 20 | for n in range(1, 10 ** 4): 21 | assert_equal(False, is_positive(-n)) 22 | assert_equal(True, is_positive(n)) 23 | -------------------------------------------------------------------------------- /ch6/decorators/syntax.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | # ONE DECORATOR 4 | def func(arg1, arg2, ...): 5 | pass 6 | 7 | func = decorator(func) 8 | 9 | # is equivalent to the following: 10 | 11 | @decorator 12 | def func(arg1, arg2, ...): 13 | pass 14 | 15 | 16 | # TWO DECORATORS 17 | def func(arg1, arg2, ...): 18 | pass 19 | 20 | func = deco1(deco2(func)) 21 | 22 | # is equivalent to the following: 23 | 24 | @deco1 25 | @deco2 26 | def func(arg1, arg2, ...): 27 | pass 28 | 29 | # DECORATOR WITH ARGUMENTS 30 | def func(arg1, arg2, ...): 31 | pass 32 | 33 | func = decoarg(arg_a, arg_b)(func) 34 | 35 | # is equivalent to the following: 36 | 37 | @decoarg(arg_a, arg_b) 38 | def func(arg1, arg2, ...): 39 | pass 40 | -------------------------------------------------------------------------------- /tests/test_ch7/test_filter_funcs_is_positive_loose.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from nose.tools import assert_equal, assert_list_equal 3 | 4 | from ch7.filter_funcs import ( 5 | filter_ints, is_positive 6 | ) 7 | 8 | 9 | class FilterIntsTestCase(TestCase): 10 | 11 | def test_filter_ints_return_value(self): 12 | v1 = [3, -4, 0, -2, 5, 0, 8, -1] 13 | v2 = [7, -3, 0, 0, 9, 1] 14 | 15 | assert_list_equal([3, 5, 8], filter_ints(v1)) 16 | assert_list_equal([7, 9, 1], filter_ints(v2)) 17 | 18 | def test_is_positive(self): 19 | assert_equal(False, is_positive(-2)) # before boundary 20 | assert_equal(False, is_positive(0)) # on the boundary 21 | assert_equal(True, is_positive(2)) # after the boundary 22 | -------------------------------------------------------------------------------- /ch3/coupons.dict.py: -------------------------------------------------------------------------------- 1 | customers = [ 2 | dict(id=1, total=200, coupon_code='F20'), # F20: fixed, £20 3 | dict(id=2, total=150, coupon_code='P30'), # P30: percent, 30% 4 | dict(id=3, total=100, coupon_code='P50'), # P50: percent, 50% 5 | dict(id=4, total=110, coupon_code='F15'), # F15: fixed, £15 6 | ] 7 | discounts = { 8 | 'F20': (0.0, 20.0), # each value is (percent, fixed) 9 | 'P30': (0.3, 0.0), 10 | 'P50': (0.5, 0.0), 11 | 'F15': (0.0, 15.0), 12 | } 13 | for customer in customers: 14 | code = customer['coupon_code'] 15 | percent, fixed = discounts.get(code, (0.0, 0.0)) 16 | customer['discount'] = percent * customer['total'] + fixed 17 | 18 | for customer in customers: 19 | print(customer['id'], customer['total'], customer['discount']) 20 | -------------------------------------------------------------------------------- /ch6/decorators/two.decorators.py: -------------------------------------------------------------------------------- 1 | from time import sleep, time 2 | from functools import wraps 3 | 4 | def measure(func): 5 | @wraps(func) 6 | def wrapper(*args, **kwargs): 7 | t = time() 8 | result = func(*args, **kwargs) 9 | print(func.__name__, 'took:', time() - t) 10 | return result 11 | return wrapper 12 | 13 | def max_result(func): 14 | @wraps(func) 15 | def wrapper(*args, **kwargs): 16 | result = func(*args, **kwargs) 17 | if result > 100: 18 | print('Result is too big ({0}). Max allowed is 100.' 19 | .format(result)) 20 | return result 21 | return wrapper 22 | 23 | @measure 24 | @max_result 25 | def cube(n): 26 | return n ** 3 27 | 28 | print(cube(2)) 29 | print(cube(5)) 30 | -------------------------------------------------------------------------------- /ch10/regex/entries/templates/entries/home.html: -------------------------------------------------------------------------------- 1 | {% extends "entries/base.html" %} 2 | {% block title%}Welcome to the Entry website.{% endblock title %} 3 | 4 | {% block page-content %} 5 |

Welcome {{ user.first_name }}!

6 | 7 |
To see the list of your entries 8 | please click here. 9 |
10 |
To insert a new entry please click 11 | here. 12 |
13 |
To login as another user please click 14 | here. 15 |
16 |
To go to the admin panel 17 | please click here. 18 |
19 | {% endblock page-content %} 20 | -------------------------------------------------------------------------------- /ch3/errorsalert.py: -------------------------------------------------------------------------------- 1 | # this example won't work unless you define a send_email function. 2 | # so I'm defining it here as a trick, please pretend you didn't 3 | # see it. ;) 4 | def send_email(*a): 5 | print (*a) 6 | 7 | alert_system = 'console' # other value can be 'email' 8 | error_severity = 'critical' # other values: 'medium' or 'low' 9 | error_message = 'OMG! Something terrible happened!' 10 | 11 | if alert_system == 'console': 12 | print(error_message) #1 13 | elif alert_system == 'email': 14 | if error_severity == 'critical': 15 | send_email('admin@example.com', error_message) #2 16 | elif error_severity == 'medium': 17 | send_email('support.1@example.com', error_message) #3 18 | else: 19 | send_email('support.2@example.com', error_message) #4 20 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templates/records/base.html: -------------------------------------------------------------------------------- 1 | {% load static from staticfiles %} 2 | 3 | 4 | 5 | 6 | 8 | 10 | {% block title %}Title{% endblock title %} 11 | 12 | 13 | 14 |
15 | {% block page-content %}{% endblock page-content %} 16 |
17 | 18 | {% block scripts %} 19 | 22 | {% endblock scripts %} 23 | 24 | 25 | -------------------------------------------------------------------------------- /ch10/regex/entries/templates/entries/base.html: -------------------------------------------------------------------------------- 1 | {% load static from staticfiles %} 2 | 3 | 4 | 5 | {% block meta %} 6 | 7 | 9 | {% endblock meta %} 10 | 11 | {% block styles %} 12 | 14 | {% endblock styles %} 15 | 16 | {% block title %}Title{% endblock title %} 17 | 18 | 19 | 20 |
21 | {% block page-content %} 22 | {% endblock page-content %} 23 |
24 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ch3/coupons.py: -------------------------------------------------------------------------------- 1 | customers = [ 2 | dict(id=1, total=200, coupon_code='F20'), # F20: fixed, £20 3 | dict(id=2, total=150, coupon_code='P30'), # P30: percent, 30% 4 | dict(id=3, total=100, coupon_code='P50'), # P50: percent, 50% 5 | dict(id=4, total=110, coupon_code='F15'), # F15: fixed, £15 6 | ] 7 | for customer in customers: 8 | code = customer['coupon_code'] 9 | if code == 'F20': 10 | customer['discount'] = 20.0 11 | elif code == 'F15': 12 | customer['discount'] = 15.0 13 | elif code == 'P30': 14 | customer['discount'] = customer['total'] * 0.3 15 | elif code == 'P50': 16 | customer['discount'] = customer['total'] * 0.5 17 | else: 18 | customer['discount'] = 0.0 19 | 20 | for customer in customers: 21 | print(customer['id'], customer['total'], customer['discount']) 22 | -------------------------------------------------------------------------------- /ch5/map.example.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | 3 | >>> map(lambda *a: a, range(3)) # without wrapping in list... 4 | # we get the iterator object 5 | >>> list(map(lambda *a: a, range(3))) # wrapping in list... 6 | [(0,), (1,), (2,)] # we get a list with its elements 7 | >>> list(map(lambda *a: a, range(3), 'abc')) # 2 iterables 8 | [(0, 'a'), (1, 'b'), (2, 'c')] 9 | >>> list(map(lambda *a: a, range(3), 'abc', range(4, 7))) # 3 10 | [(0, 'a', 4), (1, 'b', 5), (2, 'c', 6)] 11 | >>> # map stops at the shortest iterator 12 | >>> list(map(lambda *a: a, (), 'abc')) # empty tuple is shortest 13 | [] 14 | >>> list(map(lambda *a: a, (1, 2), 'abc')) # (1, 2) shortest 15 | [(1, 'a'), (2, 'b')] 16 | >>> list(map(lambda *a: a, (1, 2, 3, 4), 'abc')) # 'abc' shortest 17 | [(1, 'a'), (2, 'b'), (3, 'c')] 18 | -------------------------------------------------------------------------------- /tests/test_ch7/test_filter_funcs_refactored.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, expectedFailure 2 | from unittest.mock import patch, call 3 | from nose.tools import assert_equal, assert_list_equal 4 | 5 | from ch7.filter_funcs_refactored import filter_ints 6 | 7 | 8 | class FilterIntsTestCase(TestCase): 9 | 10 | @expectedFailure 11 | @patch('ch7.filter_funcs_refactored.is_positive') 12 | def test_filter_ints(self, is_positive_mock): 13 | v = [3, -4, 0, 5, 8] 14 | 15 | filter_ints(v) 16 | 17 | assert_equal( 18 | [call(3), call(-4), call(0), call(5), call(8)], 19 | is_positive_mock.call_args_list 20 | ) 21 | 22 | def test_filter_ints_return_value(self): 23 | v = [3, -4, 0, -2, 5, 0, 8, -1] 24 | 25 | result = filter_ints(v) 26 | 27 | assert_list_equal([3, 5, 8], result) 28 | -------------------------------------------------------------------------------- /ch5/decorate.sort.undecorate.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | 3 | 4 | students = [ 5 | dict(id=0, credits=dict(math=9, physics=6, history=7)), 6 | dict(id=1, credits=dict(math=6, physics=7, latin=10)), 7 | dict(id=2, credits=dict(history=8, physics=9, chemistry=10)), 8 | dict(id=3, credits=dict(math=5, physics=5, geography=7)), 9 | ] 10 | 11 | 12 | def decorate(student): 13 | # create a 2-tuple (sum of credits, student) from student dict 14 | return (sum(student['credits'].values()), student) 15 | pprint(decorate(students[0])) 16 | 17 | 18 | def undecorate(decorated_student): 19 | # discard sum of credits, return original student dict 20 | return decorated_student[1] 21 | 22 | 23 | students = sorted(map(decorate, students), reverse=True) 24 | students = list(map(undecorate, students)) 25 | 26 | 27 | from pprint import pprint 28 | pprint(students) 29 | -------------------------------------------------------------------------------- /ch5/performances.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | mx = 5500 # this is the max I could reach with my computer... 4 | 5 | t = time() # start time for the for loop 6 | dmloop = [] 7 | for a in range(1, mx): 8 | for b in range(a, mx): 9 | dmloop.append(divmod(a, b)) 10 | print('for loop: {:.4f} s'.format(time() - t)) # elapsed time 11 | 12 | t = time() # start time for the list comprehension 13 | dmlist = [ 14 | divmod(a, b) for a in range(1, mx) for b in range(a, mx)] 15 | print('list comprehension: {:.4f} s'.format(time() - t)) 16 | 17 | 18 | t = time() # start time for the generator expression 19 | dmgen = list( 20 | divmod(a, b) for a in range(1, mx) for b in range(a, mx)) 21 | print('generator expression: {:.4f} s'.format(time() - t)) 22 | 23 | # verify correctness of results and number of items in each list 24 | print(dmloop == dmlist == dmgen, len(dmloop)) 25 | -------------------------------------------------------------------------------- /ch10/falcon/stress.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import concurrent.futures 3 | import urllib.request 4 | 5 | URLS = ['http://127.0.0.1:8000/quote'] * 2000 6 | 7 | def load_url(url, timeout): 8 | return urllib.request.urlopen(url, timeout=timeout).read() 9 | 10 | with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: 11 | future_to_url = dict((executor.submit(load_url, url, 60), url) 12 | for url in URLS) 13 | 14 | for future in concurrent.futures.as_completed(future_to_url): 15 | url = future_to_url[future] 16 | if future.exception() is not None: 17 | print('%r generated an exception: %s' % (url, 18 | future.exception())) 19 | else: 20 | print('%r page is %d bytes' % (url, len(future.result()))) 21 | # print(future.result()) 22 | -------------------------------------------------------------------------------- /ch2/namedtuple.py: -------------------------------------------------------------------------------- 1 | # namedtuple.py 2 | 3 | 4 | # the problem 5 | >>> vision = (9.5, 8.8) 6 | >>> vision 7 | (9.5, 8.8) 8 | >>> vision[0] # left eye (implicit positional reference) 9 | 9.5 10 | >>> vision[1] # right eye (implicit positional reference) 11 | 8.8 12 | 13 | 14 | # the solution 15 | >>> from collections import namedtuple 16 | >>> Vision = namedtuple('Vision', ['left', 'right']) 17 | >>> vision = Vision(9.5, 8.8) 18 | >>> vision[0] 19 | 9.5 20 | >>> vision.left # same as vision[0], but explicit 21 | 9.5 22 | >>> vision.right # same as vision[1], but explicit 23 | 8.8 24 | 25 | 26 | # the change 27 | >>> Vision = namedtuple('Vision', ['left', 'combined', 'right']) 28 | >>> vision = Vision(9.5, 9.2, 8.8) 29 | >>> vision.left # still perfect 30 | 9.5 31 | >>> vision.right # still perfect (though now is vision[2]) 32 | 8.8 33 | >>> vision.combined # the new vision[1] 34 | 9.2 35 | -------------------------------------------------------------------------------- /ch6/oop/class.methods.split.py: -------------------------------------------------------------------------------- 1 | class String: 2 | 3 | @classmethod 4 | def is_palindrome(cls, s, case_insensitive=True): 5 | s = cls._strip_string(s) 6 | # For case insensitive comparison, we lower-case s 7 | if case_insensitive: 8 | s = s.lower() 9 | return cls._is_palindrome(s) 10 | 11 | @staticmethod 12 | def _strip_string(s): 13 | return ''.join(c for c in s if c.isalnum()) 14 | 15 | @staticmethod 16 | def _is_palindrome(s): 17 | for c in range(len(s) // 2): 18 | if s[c] != s[-c -1]: 19 | return False 20 | return True 21 | 22 | @staticmethod 23 | def get_unique_words(sentence): 24 | return set(sentence.split()) 25 | 26 | print(String.is_palindrome('A nut for a jar of tuna')) # True 27 | print(String.is_palindrome('A nut for a jar of beans')) # False 28 | -------------------------------------------------------------------------------- /tests/test_ch7/test_filter_funcs.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase # 1 2 | from unittest.mock import patch, call # 2 3 | from nose.tools import assert_equal, assert_list_equal # 3 4 | 5 | from ch7.filter_funcs import filter_ints # 4 6 | 7 | 8 | class FilterIntsTestCase(TestCase): # 5 9 | 10 | @patch('ch7.filter_funcs.is_positive') # 6 11 | def test_filter_ints(self, is_positive_mock): # 7 12 | # preparation 13 | v = [3, -4, 0, 5, 8] 14 | 15 | # execution 16 | filter_ints(v) # 8 17 | 18 | # verification 19 | assert_equal( 20 | [call(3), call(-4), call(0), call(5), call(8)], 21 | is_positive_mock.call_args_list 22 | ) # 9 23 | 24 | def test_filter_ints_return_value(self): 25 | v = [3, -4, 0, -2, 5, 0, 8, -1] 26 | 27 | result = filter_ints(v) 28 | 29 | assert_list_equal([3, 5, 8], result) 30 | -------------------------------------------------------------------------------- /ch7/exceptions/first.example.py: -------------------------------------------------------------------------------- 1 | # This is not a valid Python module - Don't run it. 2 | # a few examples of exceptions 3 | 4 | >>> gen = (n for n in range(2)) 5 | >>> next(gen) 6 | 0 7 | >>> next(gen) 8 | 1 9 | >>> next(gen) 10 | Traceback (most recent call last): 11 | File "", line 1, in 12 | StopIteration 13 | >>> print(undefined_var) 14 | Traceback (most recent call last): 15 | File "", line 1, in 16 | NameError: name 'undefined_var' is not defined 17 | >>> mylist = [1, 2, 3] 18 | >>> mylist[5] 19 | Traceback (most recent call last): 20 | File "", line 1, in 21 | IndexError: list index out of range 22 | >>> mydict = {'a': 'A', 'b': 'B'} 23 | >>> mydict['c'] 24 | Traceback (most recent call last): 25 | File "", line 1, in 26 | KeyError: 'c' 27 | >>> 1 / 0 28 | Traceback (most recent call last): 29 | File "", line 1, in 30 | ZeroDivisionError: division by zero 31 | -------------------------------------------------------------------------------- /ch2/chainmap.py: -------------------------------------------------------------------------------- 1 | # chainmap.py 2 | 3 | 4 | >>> from collections import ChainMap 5 | >>> default_connection = {'host': 'localhost', 'port': 4567} 6 | >>> connection = {'port': 5678} 7 | >>> conn = ChainMap(connection, default_connection) # map creation 8 | >>> conn['port'] # port is found in the first dictionary 9 | 5678 10 | >>> conn['host'] # host is fetched from the second dictionary 11 | 'localhost' 12 | >>> conn.maps # we can see the mapping objects 13 | [{'port': 5678}, {'host': 'localhost', 'port': 4567}] 14 | >>> conn['host'] = 'packtpub.com' # let's add host 15 | >>> conn.maps 16 | [{'host': 'packtpub.com', 'port': 5678}, 17 | {'host': 'localhost', 'port': 4567}] 18 | >>> del conn['port'] # let's remove the port information 19 | >>> conn.maps 20 | [{'host': 'packtpub.com'}, 21 | {'host': 'localhost', 'port': 4567}] 22 | >>> conn['port'] # now port is fetched from the second dictionary 23 | 4567 24 | >>> dict(conn) # easy to merge and convert to regular dictionary 25 | {'host': 'packtpub.com', 'port': 4567} -------------------------------------------------------------------------------- /tests/test_ch7/test_data_flatten.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from nose.tools import assert_equal 3 | 4 | from ch7.data_flatten import flatten 5 | 6 | 7 | class FlattenTestCase(TestCase): 8 | 9 | def test_flatten(self): 10 | test_cases = [ 11 | ({'A': {'B': 'C', 'D': [1, 2, 3], 'E': {'F': 'G'}}, 12 | 'H': 3.14, 13 | 'J': ['K', 'L'], 14 | 'M': 'N'}, 15 | {'A.B': 'C', 16 | 'A.D': [1, 2, 3], 17 | 'A.E.F': 'G', 18 | 'H': 3.14, 19 | 'J': ['K', 'L'], 20 | 'M': 'N'}), 21 | (0, 0), 22 | ('Hello', 'Hello'), 23 | ({'A': None}, {'A': None}), 24 | ] 25 | for (nested, flat) in test_cases: 26 | assert_equal(flat, flatten(nested)) 27 | 28 | def test_flatten_custom_separator(self): 29 | nested = {'A': {'B': {'C': 'D'}}} 30 | 31 | assert_equal( 32 | {'A#B#C': 'D'}, flatten(nested, separator='#')) 33 | -------------------------------------------------------------------------------- /ch12/pwdweb/pwdweb/urls.py: -------------------------------------------------------------------------------- 1 | """pwdweb URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add an import: from blog import urls as blog_urls 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | 19 | from records import urls as records_url 20 | from records.views import HomeView 21 | 22 | urlpatterns = [ 23 | url(r'^admin/', include(admin.site.urls)), 24 | url(r'^records/', include(records_url, namespace='records')), 25 | url(r'^$', HomeView.as_view(), name='home'), 26 | ] 27 | -------------------------------------------------------------------------------- /ch10/regex/entries/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Entry', 18 | fields=[ 19 | ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), 20 | ('pattern', models.CharField(max_length=255)), 21 | ('test_string', models.CharField(max_length=255)), 22 | ('date_added', models.DateTimeField(default=django.utils.timezone.now)), 23 | ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 24 | ], 25 | options={ 26 | 'verbose_name_plural': 'entries', 27 | }, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /ch6/iterators/iterator.py: -------------------------------------------------------------------------------- 1 | class OddEven: 2 | 3 | def __init__(self, data): 4 | self._data = data 5 | self.indexes = (list(range(0, len(data), 2)) + 6 | list(range(1, len(data), 2))) 7 | 8 | def __iter__(self): 9 | return self 10 | 11 | def __next__(self): 12 | if self.indexes: 13 | return self._data[self.indexes.pop(0)] 14 | raise StopIteration 15 | 16 | oddeven = OddEven('ThIsIsCoOl!') 17 | print(''.join(c for c in oddeven)) # TIICO!hssol 18 | 19 | oddeven = OddEven('HoLa') # or manually... 20 | it = iter(oddeven) # this calls oddeven.__iter__ internally 21 | print(next(it)) # H 22 | print(next(it)) # L 23 | print(next(it)) # o 24 | print(next(it)) # a 25 | 26 | 27 | # make sure it works correctly with edge cases 28 | oddeven = OddEven('') 29 | print(' '.join(c for c in oddeven)) 30 | 31 | oddeven = OddEven('A') 32 | print(' '.join(c for c in oddeven)) 33 | 34 | oddeven = OddEven('Ab') 35 | print(' '.join(c for c in oddeven)) 36 | 37 | oddeven = OddEven('AbC') 38 | print(' '.join(c for c in oddeven)) 39 | -------------------------------------------------------------------------------- /ch6/oop/property.py: -------------------------------------------------------------------------------- 1 | class Person: 2 | def __init__(self, age): 3 | self.age = age # anyone can modify this freely 4 | 5 | class PersonWithAccessors: 6 | def __init__(self, age): 7 | self._age = age 8 | 9 | def get_age(self): 10 | return self._age 11 | 12 | def set_age(self, age): 13 | if 18 <= age <= 99: 14 | self._age = age 15 | else: 16 | raise ValueError('Age must be within [18, 99]') 17 | 18 | class PersonPythonic: 19 | def __init__(self, age): 20 | self._age = age 21 | 22 | @property 23 | def age(self): 24 | return self._age 25 | 26 | @age.setter 27 | def age(self, age): 28 | if 18 <= age <= 99: 29 | self._age = age 30 | else: 31 | raise ValueError('Age must be within [18, 99]') 32 | 33 | person = PersonPythonic(39) 34 | print(person.age) # 39 - Notice we access as data attribute 35 | person.age = 42 # Notice we access as data attribute 36 | print(person.age) # 42 37 | person.age = 100 # ValueError: Age must be within [18, 99] 38 | -------------------------------------------------------------------------------- /ch11/ipdebugger_ipdb.py: -------------------------------------------------------------------------------- 1 | # d comes from a JSON payload we don't control 2 | d = {'first': 'v1', 'second': 'v2', 'fourth': 'v4'} 3 | # keys also comes from a JSON payload we don't control 4 | keys = ('first', 'second', 'third', 'fourth') 5 | 6 | def do_something_with_value(value): 7 | print(value) 8 | 9 | import ipdb 10 | ipdb.set_trace() # we place a breakpoint here 11 | 12 | for key in keys: 13 | do_something_with_value(d[key]) 14 | 15 | print('Validation done.') 16 | 17 | 18 | """ 19 | $ python ipdebugger_ipdb.py 20 | > /home/fab/srv/l.p/ch11/ipdebugger_ipdb.py(12)() 21 | 11 22 | ---> 12 for key in keys: # this is where the breakpoint comes 23 | 13 do_something_with_value(d[key]) 24 | 25 | ipdb> keys # let's inspect the keys tuple 26 | ('first', 'second', 'third', 'fourth') 27 | ipdb> !d.keys() # now the keys of d 28 | dict_keys(['first', 'fourth', 'second']) # we miss 'third' 29 | ipdb> !d['third'] = 'something dark side...' # let's put it in 30 | ipdb> c # ... and continue 31 | v1 32 | v2 33 | something dark side... 34 | v4 35 | Validation done. 36 | """ 37 | -------------------------------------------------------------------------------- /ch6/oop/static.methods.py: -------------------------------------------------------------------------------- 1 | class String: 2 | 3 | @staticmethod 4 | def is_palindrome(s, case_insensitive=True): 5 | # we allow only letters and numbers 6 | s = ''.join(c for c in s if c.isalnum()) # Study this! 7 | # For case insensitive comparison, we lower-case s 8 | if case_insensitive: 9 | s = s.lower() 10 | for c in range(len(s) // 2): 11 | if s[c] != s[-c -1]: 12 | return False 13 | return True 14 | 15 | @staticmethod 16 | def get_unique_words(sentence): 17 | return set(sentence.split()) 18 | 19 | print(String.is_palindrome( 20 | 'Radar', case_insensitive=False)) # False: Case Sensitive 21 | print(String.is_palindrome('A nut for a jar of tuna')) # True 22 | print(String.is_palindrome('Never Odd, Or Even!')) # True 23 | print(String.is_palindrome( 24 | 'In Girum Imus Nocte Et Consumimur Igni') # Latin! Show-off! 25 | ) # True 26 | 27 | print(String.get_unique_words( 28 | 'I love palindromes. I really really love them!')) 29 | # {'them!', 'really', 'palindromes.', 'I', 'love'} 30 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/static/records/css/main.css: -------------------------------------------------------------------------------- 1 | /* HTML tags */ 2 | html, body, * { 3 | font-family: 'Trebuchet MS', Helvetica, sans-serif; } 4 | a { color: #333; } 5 | .record { 6 | clear: both; padding: 1em; border-bottom: 1px solid #666;} 7 | .record-left { float: left; width: 300px;} 8 | .record-list { padding: 2px 0; } 9 | .fieldWrapper { padding: 5px; } 10 | .footer { margin-top: 1em; color: #333; } 11 | .home-option { padding: .6em 0; } 12 | .record-span { font-weight: bold; padding-right: 1em; } 13 | .record-notes { vertical-align: top; } 14 | .record-list-actions { padding: 4px 0; clear: both; } 15 | .record-list-actions a { padding: 0 4px; } 16 | #pwd-info { padding: 0 6px; font-size: 1.1em; font-weight: bold;} 17 | #id_notes { vertical-align: top; } 18 | /* Messages */ 19 | .success, .errorlist {font-size: 1.2em; font-weight: bold; } 20 | .success {color: #25B725; } 21 | .errorlist {color: #B12B2B; } 22 | /* colors */ 23 | .row-light-blue { background-color: #E6F0FA; } 24 | .row-white { background-color: #fff; } 25 | .green { color: #060; } 26 | .orange { color: #FF3300; } 27 | .red { color: #900; } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Record', 15 | fields=[ 16 | ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), 17 | ('title', models.CharField(unique=True, max_length=64)), 18 | ('username', models.CharField(max_length=64)), 19 | ('email', models.EmailField(max_length=254, null=True, blank=True)), 20 | ('url', models.URLField(max_length=255, null=True, blank=True)), 21 | ('password', models.CharField(max_length=2048)), 22 | ('notes', models.TextField(null=True, blank=True)), 23 | ('created', models.DateTimeField(auto_now_add=True)), 24 | ('last_modified', models.DateTimeField(auto_now=True)), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /ch11/log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig( 4 | filename='ch11.log', 5 | level=logging.DEBUG, # minimum level capture in the file 6 | format='[%(asctime)s] %(levelname)s:%(message)s', 7 | datefmt='%m/%d/%Y %I:%M:%S %p') 8 | 9 | mylist = [1, 2, 3] 10 | logging.info('Starting to process `mylist`...') 11 | 12 | for position in range(4): 13 | try: 14 | logging.debug('Value at position {} is {}'.format( 15 | position, mylist[position])) 16 | except IndexError: 17 | logging.exception('Faulty position: {}'.format(position)) 18 | 19 | logging.info('Done parsing `mylist`.') 20 | 21 | 22 | """ ch11.log 23 | [10/08/2015 04:25:59 PM] INFO:Starting to process `mylist`... 24 | [10/08/2015 04:25:59 PM] DEBUG:Value at position 0 is 1 25 | [10/08/2015 04:25:59 PM] DEBUG:Value at position 1 is 2 26 | [10/08/2015 04:25:59 PM] DEBUG:Value at position 2 is 3 27 | [10/08/2015 04:25:59 PM] ERROR:Faulty position: 3 28 | Traceback (most recent call last): 29 | File "log.py", line 15, in 30 | position, mylist[position])) 31 | IndexError: list index out of range 32 | [10/08/2015 04:25:59 PM] INFO:Done parsing `mylist`. 33 | """ 34 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/urls.py: -------------------------------------------------------------------------------- 1 | """pwdweb URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add an import: from blog import urls as blog_urls 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | 19 | from .views import (RecordCreateView, RecordUpdateView, 20 | RecordDeleteView, RecordListView) 21 | 22 | 23 | urlpatterns = [ 24 | url(r'^add/$', RecordCreateView.as_view(), name='add'), 25 | url(r'^edit/(?P[0-9]+)/$', RecordUpdateView.as_view(), 26 | name='edit'), 27 | url(r'^delete/(?P[0-9]+)/$', RecordDeleteView.as_view(), 28 | name='delete'), 29 | url(r'^$', RecordListView.as_view(), name='list'), 30 | ] 31 | -------------------------------------------------------------------------------- /ch2/final_considerations.py: -------------------------------------------------------------------------------- 1 | # final_considerations.py 2 | 3 | 4 | >>> a = 1000000 5 | >>> b = 1000000 6 | >>> id(a) == id(b) 7 | False 8 | 9 | 10 | >>> a = 5 11 | >>> b = 5 12 | >>> id(a) == id(b) 13 | True 14 | 15 | 16 | # how to choose data structures 17 | # example customer objects 18 | customer1 = {'id': 'abc123', 'full_name': 'Master Yoda'} 19 | customer2 = {'id': 'def456', 'full_name': 'Obi-Wan Kenobi'} 20 | customer3 = {'id': 'ghi789', 'full_name': 'Anakin Skywalker'} 21 | # collect them in a tuple 22 | customers = (customer1, customer2, customer3) 23 | # or collect them in a list 24 | customers = [customer1, customer2, customer3] 25 | # or maybe within a dictionary, they have a unique id after all 26 | customers = { 27 | 'abc123': customer1, 28 | 'def456': customer2, 29 | 'ghi789': customer3, 30 | } 31 | 32 | 33 | # negative indexing 34 | >>> a = list(range(10)) # `a` has 10 elements. Last one is 9. 35 | >>> a 36 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 37 | >>> len(a) # its length is 10 elements 38 | 10 39 | >>> a[len(a) - 1] # position of last one is len(a) - 1 40 | 9 41 | >>> a[-1] # but we don't need len(a)! Python rocks! 42 | 9 43 | >>> a[-2] # equivalent to len(a) - 2 44 | 8 45 | >>> a[-3] # equivalent to len(a) - 3 46 | 7 47 | -------------------------------------------------------------------------------- /ch10/falcon/quotes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | quotes = [ 3 | "Thousands of candles can be lighted from a single candle, " 4 | "and the life of the candle will not be shortened. " 5 | "Happiness never decreases by being shared.", 6 | "You will not be punished for your anger, " 7 | "you will be punished by your anger.", 8 | "Peace comes from within. Do not seek it without.", 9 | "The mind is everything. What you think you become.", 10 | "No one saves us but ourselves. No one can and no one may. " 11 | "We ourselves must walk the path.", 12 | "Do not dwell in the past, do not dream of the future, " 13 | "concentrate the mind on the present moment.", 14 | "We are shaped by our thoughts; we become what we think. " 15 | "When the mind is pure, joy follows like a shadow that " 16 | "never leaves.", 17 | "There are only two mistakes one can make along the road " 18 | "to truth; not going all the way, and not starting.", 19 | "The whole secret of existence is to have no fear. " 20 | "Never fear what will become of you, depend on no one. " 21 | "Only the moment you reject all help are you freed.", 22 | "However many holy words you read, however many you speak, " 23 | "what good will they do you if you do not act on upon them?", 24 | ] 25 | -------------------------------------------------------------------------------- /ch7/data_flatten.py: -------------------------------------------------------------------------------- 1 | nested = { 2 | 'fullname': 'Alessandra', 3 | 'age': 41, 4 | 'phone-numbers': ['+447421234567', '+447423456789'], 5 | 'residence': { 6 | 'address': { 7 | 'first-line': 'Alexandra Rd', 8 | 'second-line': '', 9 | }, 10 | 'zip': 'N8 0PP', 11 | 'city': 'London', 12 | 'country': 'UK', 13 | }, 14 | } 15 | 16 | flat = { 17 | 'fullname': 'Alessandra', 18 | 'age': 41, 19 | 'phone-numbers': ['+447421234567', '+447423456789'], 20 | 'residence.address.first-line': 'Alexandra Rd', 21 | 'residence.address.second-line': '', 22 | 'residence.zip': 'N8 0PP', 23 | 'residence.city': 'London', 24 | 'residence.country': 'UK', 25 | } 26 | 27 | 28 | def flatten(data, prefix='', separator='.'): 29 | """Flattens a nested dict structure. """ 30 | if not isinstance(data, dict): 31 | return {prefix: data} if prefix else data 32 | 33 | result = {} 34 | for (key, value) in data.items(): 35 | result.update( 36 | flatten( 37 | value, 38 | _get_new_prefix(prefix, key, separator), 39 | separator=separator)) 40 | 41 | return result 42 | 43 | 44 | def _get_new_prefix(prefix, key, separator): 45 | return (separator.join((prefix, str(key))) 46 | if prefix else str(key)) 47 | 48 | 49 | if __name__ == "__main__": 50 | print(flatten(nested)) 51 | -------------------------------------------------------------------------------- /ch6/oop/class.inheritance.py: -------------------------------------------------------------------------------- 1 | class Engine(): 2 | def start(self): 3 | pass 4 | 5 | def stop(self): 6 | pass 7 | 8 | class ElectricEngine(Engine): # Is-A Engine 9 | pass 10 | 11 | class V8Engine(Engine): # Is-A Engine 12 | pass 13 | 14 | class Car(): 15 | engine_cls = Engine 16 | 17 | def __init__(self): 18 | self.engine = self.engine_cls() # Has-A Engine 19 | 20 | def start(self): 21 | print( 22 | 'Starting engine {0} for car {1}... Wroom, wroom!' 23 | .format( 24 | self.engine.__class__.__name__, 25 | self.__class__.__name__) 26 | ) 27 | self.engine.start() 28 | 29 | def stop(self): 30 | self.engine.stop() 31 | 32 | class RaceCar(Car): # Is-A Car 33 | engine_cls = V8Engine 34 | 35 | class CityCar(Car): # Is-A Car 36 | engine_cls = ElectricEngine 37 | 38 | class F1Car(RaceCar): # Is-A RaceCar and also Is-A Car 39 | engine_cls = V8Engine 40 | 41 | car = Car() 42 | racecar = RaceCar() 43 | citycar = CityCar() 44 | f1car = F1Car() 45 | cars = [car, racecar, citycar, f1car] 46 | 47 | for car in cars: 48 | car.start() 49 | 50 | """ Prints: 51 | Starting engine Engine for car Car... Wroom, wroom! 52 | Starting engine V8Engine for car RaceCar... Wroom, wroom! 53 | Starting engine ElectricEngine for car CityCar... Wroom, wroom! 54 | Starting engine V8Engine for car F1Car... Wroom, wroom! 55 | """ 56 | -------------------------------------------------------------------------------- /ch10/regex/entries/templates/entries/list.html: -------------------------------------------------------------------------------- 1 | {% extends "entries/base.html" %} 2 | {% block title%} Entries list {% endblock title %} 3 | 4 | {% block page-content %} 5 | {% if entries %} 6 |

Your entries ({{ entries|length }} found)

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for entry, match in entries %} 15 | 16 | 23 | 33 | 34 | {% endfor %} 35 | 36 |
EntryMatches
17 | Pattern: 18 | "{{ entry.pattern }}"
19 | Test String: 20 | "{{ entry.test_string }}"
21 | Added: {{ entry.date_added }} 22 |
24 | {% if match %} 25 | Group: {{ match.0 }}
26 | Subgroups: 27 | {{ match.1|default_if_none:"none" }}
28 | Group Dict: {{ match.2|default_if_none:"none" }} 29 | {% else %} 30 | No matches found. 31 | {% endif %} 32 |
37 | {% else %} 38 |

You have no entries

39 | 40 | {% endif %} 41 | {% endblock page-content %} 42 | 43 | {% block footer %} 44 | {% include "entries/footer.html" %} 45 | {% endblock footer %} 46 | -------------------------------------------------------------------------------- /ch10/regex/regex/urls.py: -------------------------------------------------------------------------------- 1 | """regex URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add an import: from blog import urls as blog_urls 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | from django.contrib.auth import views as auth_views 19 | from django.core.urlresolvers import reverse_lazy 20 | 21 | from entries.views import HomeView, EntryListView, EntryFormView 22 | 23 | 24 | urlpatterns = [ 25 | url(r'^admin/', include(admin.site.urls)), 26 | url(r'^entries/$', EntryListView.as_view(), name='entries'), 27 | url(r'^entries/insert$', 28 | EntryFormView.as_view(), 29 | name='insert'), 30 | 31 | url(r'^login/$', 32 | auth_views.login, 33 | kwargs={'template_name': 'admin/login.html'}, 34 | name='login'), 35 | url(r'^logout/$', 36 | auth_views.logout, 37 | kwargs={'next_page': reverse_lazy('home')}, 38 | name='logout'), 39 | 40 | url(r'^$', HomeView.as_view(), name='home'), 41 | ] 42 | -------------------------------------------------------------------------------- /ch2/sets.py: -------------------------------------------------------------------------------- 1 | # sets.py 2 | 3 | 4 | >>> small_primes = set() # empty set 5 | >>> small_primes.add(2) # adding one element at a time 6 | >>> small_primes.add(3) 7 | >>> small_primes.add(5) 8 | >>> small_primes 9 | {2, 3, 5} 10 | >>> small_primes.add(1) # Look what I've done, 1 is not a prime! 11 | >>> small_primes 12 | {1, 2, 3, 5} 13 | >>> small_primes.remove(1) # so let's remove it 14 | >>> 3 in small_primes # membership test 15 | True 16 | >>> 4 in small_primes 17 | False 18 | >>> 4 not in small_primes # negated membership test 19 | True 20 | >>> small_primes.add(3) # trying to add 3 again 21 | >>> small_primes 22 | {2, 3, 5} # no change, duplication is not allowed 23 | >>> bigger_primes = set([5, 7, 11, 13]) # faster creation 24 | >>> small_primes | bigger_primes # union operator `|` 25 | {2, 3, 5, 7, 11, 13} 26 | >>> small_primes & bigger_primes # intersection operator `&` 27 | {5} 28 | >>> small_primes - bigger_primes # difference operator `-` 29 | {2, 3} 30 | 31 | 32 | # frozenset 33 | >>> small_primes = frozenset([2, 3, 5, 7]) 34 | >>> bigger_primes = frozenset([5, 7, 11]) 35 | >>> small_primes.add(11) # we cannot add to a frozenset 36 | Traceback (most recent call last): 37 | File "", line 1, in 38 | AttributeError: 'frozenset' object has no attribute 'add' 39 | >>> small_primes.remove(2) # neither we can remove 40 | Traceback (most recent call last): 41 | File "", line 1, in 42 | AttributeError: 'frozenset' object has no attribute 'remove' 43 | >>> small_primes & bigger_primes # intersect, union, etc. allowed 44 | frozenset({5, 7}) 45 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/static/records/js/api.js: -------------------------------------------------------------------------------- 1 | var baseURL = 'http://127.0.0.1:5555/password'; 2 | 3 | var getRandomPassword = function() { 4 | var apiURL = '{url}/generate'.replace('{url}', baseURL); 5 | $.ajax({ 6 | type: 'GET', 7 | url: apiURL, 8 | success: function(data, status, request) { 9 | $('#id_password').val(data[1]); 10 | }, 11 | error: function() { alert('Unexpected error'); } 12 | }); 13 | } 14 | 15 | $(function() { 16 | $('#generate-btn').click(getRandomPassword); 17 | }); 18 | 19 | var validatePassword = function() { 20 | var apiURL = '{url}/validate'.replace('{url}', baseURL); 21 | $.ajax({ 22 | type: 'POST', 23 | url: apiURL, 24 | data: JSON.stringify({'password': $('#id_password').val()}), 25 | contentType: "text/plain", // Avoid CORS preflight 26 | success: function(data, status, request) { 27 | var valid = data['valid'], infoClass, grade; 28 | var msg = (valid?'Valid':'Invalid') + ' password.'; 29 | if (valid) { 30 | var score = data['score']['total']; 31 | grade = (score<10?'Poor':(score<18?'Medium':'Strong')); 32 | infoClass = (score<10?'red':(score<18?'orange':'green')); 33 | msg += ' (Score: {score}, {grade})' 34 | .replace('{score}', score).replace('{grade}', grade); 35 | } 36 | $('#pwd-info').html(msg); 37 | $('#pwd-info').removeClass().addClass(infoClass); 38 | }, 39 | error: function(data) { alert('Unexpected error'); } 40 | }); 41 | } 42 | 43 | $(function() { 44 | $('#validate-btn').click(validatePassword); 45 | }); 46 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from cryptography.fernet import Fernet 3 | 4 | from django.conf import settings 5 | from django.db import models 6 | 7 | 8 | class Record(models.Model): 9 | 10 | DEFAULT_ENCODING = 'utf-8' 11 | 12 | title = models.CharField(max_length=64, unique=True) 13 | username = models.CharField(max_length=64) 14 | email = models.EmailField(null=True, blank=True) 15 | url = models.URLField(max_length=255, null=True, blank=True) 16 | password = models.CharField(max_length=2048) 17 | notes = models.TextField(null=True, blank=True) 18 | created = models.DateTimeField(auto_now_add=True) 19 | last_modified = models.DateTimeField(auto_now=True) 20 | 21 | def encrypt_password(self): 22 | self.password = self.encrypt(self.password) 23 | 24 | def decrypt_password(self): 25 | self.password = self.decrypt(self.password) 26 | 27 | def encrypt(self, plaintext): 28 | return self.cypher('encrypt', plaintext) 29 | 30 | def decrypt(self, cyphertext): 31 | return self.cypher('decrypt', cyphertext) 32 | 33 | def cypher(self, cypher_func, text): 34 | fernet = Fernet(settings.ENCRYPTION_KEY) 35 | result = getattr(fernet, cypher_func)( 36 | self._to_bytes(text)) 37 | return self._to_str(result) 38 | 39 | def _to_str(self, bytes_str): 40 | return bytes_str.decode(self.DEFAULT_ENCODING) 41 | 42 | def _to_bytes(self, s): 43 | return s.encode(self.DEFAULT_ENCODING) 44 | 45 | def __str__(self): 46 | return '{}'.format(self.title) 47 | -------------------------------------------------------------------------------- /ch6/oop/multiple.inheritance.py: -------------------------------------------------------------------------------- 1 | class Shape: 2 | geometric_type = 'Generic Shape' 3 | 4 | def area(self): # This acts as placeholder for the interface 5 | raise NotImplementedError 6 | 7 | def get_geometric_type(self): 8 | return self.geometric_type 9 | 10 | 11 | class Plotter: 12 | 13 | def plot(self, ratio, topleft): 14 | # Imagine some nice plotting logic here... 15 | print('Plotting at {}, ratio {}.'.format( 16 | topleft, ratio)) 17 | 18 | 19 | class Polygon(Shape, Plotter): # base class for polygons 20 | geometric_type = 'Polygon' 21 | 22 | class RegularPolygon(Polygon): # Is-A Polygon 23 | geometric_type = 'Regular Polygon' 24 | 25 | def __init__(self, side): 26 | self.side = side 27 | 28 | 29 | class RegularHexagon(RegularPolygon): # Is-A RegularPolygon 30 | geometric_type = 'RegularHexagon' 31 | 32 | def area(self): 33 | return 1.5 * (3 ** .5 * self.side ** 2) 34 | 35 | 36 | class Square(RegularPolygon): # Is-A RegularPolygon 37 | geometric_type = 'Square' 38 | 39 | def area(self): 40 | return self.side * self.side 41 | 42 | 43 | hexagon = RegularHexagon(10) 44 | print(hexagon.area()) # 259.8076211353316 45 | print(hexagon.get_geometric_type()) # RegularHexagon 46 | hexagon.plot(0.8, (75, 77)) # Plotting at (75, 77), ratio 0.8. 47 | 48 | square = Square(12) 49 | print(square.area()) # 144 50 | print(square.get_geometric_type()) # Square 51 | square.plot(0.93, (74, 75)) # Plotting at (74, 75), ratio 0.93. 52 | 53 | print(square.__class__.__mro__) 54 | # prints: 55 | # (, , 56 | # , , 57 | # , ) 58 | -------------------------------------------------------------------------------- /ch2/sequences.py: -------------------------------------------------------------------------------- 1 | # sequences.py 2 | 3 | 4 | # strings 5 | >>> # 4 ways to make a string 6 | >>> str1 = 'This is a string. We built it with single quotes.' 7 | >>> str2 = "This is also a string, but built with double quotes." 8 | >>> str3 = '''This is built using triple quotes, 9 | ... so it can span multiple lines.''' 10 | >>> str4 = """This too 11 | ... is a multiline one 12 | ... built with triple double-quotes.""" 13 | >>> str4 #A 14 | 'This too\nis a multiline one\nbuilt with triple double-quotes.' 15 | >>> print(str4) #B 16 | This too 17 | is a multiline one 18 | built with triple double-quotes. 19 | >>> 20 | 21 | 22 | # encode / decode 23 | >>> s = "This is üŋíc0de" # unicode string: code points 24 | >>> type(s) 25 | 26 | >>> encoded_s = s.encode('utf-8') # utf-8 encoded version of s 27 | >>> encoded_s 28 | b'This is \xc3\xbc\xc5\x8b\xc3\xadc0de' # result: bytes object 29 | >>> type(encoded_s) # another way to verify it 30 | 31 | >>> encoded_s.decode('utf-8') # let's revert to the original 32 | 'This is üŋíc0de' 33 | >>> bytes_obj = b"A bytes object" # a bytes object 34 | >>> type(bytes_obj) 35 | 36 | 37 | 38 | # length 39 | >>> len(str1) 40 | 49 41 | 42 | 43 | # indexing and slicing 44 | >>> s = "The trouble is you think you have time." 45 | >>> s[0] # indexing at position 0, which is the first char 46 | 'T' 47 | >>> s[5] # indexing at position 5, which is the sixth char 48 | 'r' 49 | >>> s[:4] # slicing, we specify only the stop position 50 | 'The ' 51 | >>> s[4:] # slicing, we specify only the start position 52 | 'trouble is you think you have time.' 53 | >>> s[2:14] # slicing, both start and stop positions 54 | 'e trouble is' 55 | >>> s[2:14:3] # slicing, start, stop and step (every 3 chars) 56 | 'erb ' 57 | >>> s[:] # quick way of making a copy 58 | 'The trouble is you think you have time.' 59 | -------------------------------------------------------------------------------- /ch9/example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "def fibonacci(N):\n", 12 | " a, b = 0, 1\n", 13 | " while a < N:\n", 14 | " yield a\n", 15 | " a, b = b, a + b" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 2, 21 | "metadata": { 22 | "collapsed": false 23 | }, 24 | "outputs": [ 25 | { 26 | "data": { 27 | "text/plain": [ 28 | "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]" 29 | ] 30 | }, 31 | "execution_count": 2, 32 | "metadata": {}, 33 | "output_type": "execute_result" 34 | } 35 | ], 36 | "source": [ 37 | "list(fibonacci(100))" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 3, 43 | "metadata": { 44 | "collapsed": false 45 | }, 46 | "outputs": [ 47 | { 48 | "name": "stdout", 49 | "output_type": "stream", 50 | "text": [ 51 | "The slowest run took 11.15 times longer than the fastest. This could mean that an intermediate result is being cached \n", 52 | "10000000 loops, best of 3: 192 ns per loop\n" 53 | ] 54 | } 55 | ], 56 | "source": [ 57 | "%timeit fibonacci(10**4)" 58 | ] 59 | } 60 | ], 61 | "metadata": { 62 | "kernelspec": { 63 | "display_name": "Python 3", 64 | "language": "python", 65 | "name": "python3" 66 | }, 67 | "language_info": { 68 | "codemirror_mode": { 69 | "name": "ipython", 70 | "version": 3 71 | }, 72 | "file_extension": ".py", 73 | "mimetype": "text/x-python", 74 | "name": "python", 75 | "nbconvert_exporter": "python", 76 | "pygments_lexer": "ipython3", 77 | "version": "3.4.3" 78 | } 79 | }, 80 | "nbformat": 4, 81 | "nbformat_minor": 0 82 | } 83 | -------------------------------------------------------------------------------- /ch4/primes.py: -------------------------------------------------------------------------------- 1 | from math import sqrt, ceil 2 | 3 | 4 | def get_primes(n): 5 | """Calculate a list of primes up to n (included). """ 6 | primelist = [] 7 | for candidate in range(2, n + 1): 8 | is_prime = True 9 | root = int(ceil(sqrt(candidate))) # division limit 10 | for prime in primelist: # we try only the primes 11 | if prime > root: # no need to check any further 12 | break 13 | if candidate % prime == 0: 14 | is_prime = False 15 | break 16 | if is_prime: 17 | primelist.append(candidate) 18 | return primelist 19 | 20 | 21 | if __name__ == "__main__": 22 | 23 | def test(): 24 | primes = get_primes(10**3) 25 | primes2 = [ 26 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 27 | 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 28 | 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 29 | 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 30 | 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 31 | 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 32 | 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 33 | 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 34 | 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 35 | 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 36 | 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 37 | 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 38 | 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 39 | 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 40 | 941, 947, 953, 967, 971, 977, 983, 991, 997 41 | ] 42 | return primes == primes2 43 | 44 | print(test()) 45 | 46 | print(get_primes(100)) 47 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templates/records/list.html: -------------------------------------------------------------------------------- 1 | {% extends "records/base.html" %} 2 | {% load record_extras %} 3 | {% block title %}Records{% endblock title %} 4 | 5 | {% block page-content %} 6 |

Records

7 | {% include "records/messages.html" %} 8 | 9 | {% for record in records %} 10 |
12 |
13 |
14 | Title{{ record.title }} 15 |
16 |
17 | Username 18 | {{ record.username }} 19 |
20 |
21 | Email{{ record.email }} 22 |
23 |
24 | URL 25 | 26 | {{ record.url }} 27 |
28 |
29 | Password 30 | {% hide_password record.plaintext %} 31 |
32 |
33 |
34 |
35 | Notes 36 | 38 |
39 |
40 | Last modified 41 | {{ record.last_modified }} 42 |
43 |
44 | Created 45 | {{ record.created }} 46 |
47 | 48 |
49 |
50 | » edit 51 | » delete 52 | 53 |
54 |
55 | {% endfor %} 56 | {% endblock page-content %} 57 | 58 | {% block footer %} 59 |

Go back to top

60 | {% include "records/footer.html" %} 61 | {% endblock footer %} 62 | -------------------------------------------------------------------------------- /ch6/oop/class.issubclass.isinstance.py: -------------------------------------------------------------------------------- 1 | class Engine(): 2 | def start(self): 3 | pass 4 | 5 | def stop(self): 6 | pass 7 | 8 | class ElectricEngine(Engine): # Is-A Engine 9 | pass 10 | 11 | class V8Engine(Engine): # Is-A Engine 12 | pass 13 | 14 | 15 | class Car(): 16 | engine_cls = Engine 17 | 18 | def __init__(self): 19 | self.engine = self.engine_cls() # Has-A Engine 20 | 21 | def start(self): 22 | print( 23 | 'Starting engine {0} for car {1}... Wroom, wroom!' 24 | .format( 25 | self.engine.__class__.__name__, 26 | self.__class__.__name__) 27 | ) 28 | self.engine.start() 29 | 30 | def stop(self): 31 | self.engine.stop() 32 | 33 | class RaceCar(Car): # Is-A Car 34 | engine_cls = V8Engine 35 | 36 | class CityCar(Car): # Is-A Car 37 | engine_cls = ElectricEngine 38 | 39 | class F1Car(RaceCar): # Is-A RaceCar and also Is-A Car 40 | engine_cls = V8Engine 41 | 42 | 43 | car = Car() 44 | racecar = RaceCar() 45 | f1car = F1Car() 46 | cars = [(car, 'car'), (racecar, 'racecar'), (f1car, 'f1car')] 47 | car_classes = [Car, RaceCar, F1Car] 48 | 49 | for car, car_name in cars: 50 | for class_ in car_classes: 51 | belongs = isinstance(car, class_) 52 | msg = 'is a' if belongs else 'is not a' 53 | print(car_name, msg, class_.__name__) 54 | 55 | """ Prints: 56 | car is a Car 57 | car is not a RaceCar 58 | car is not a F1Car 59 | racecar is a Car 60 | racecar is a RaceCar 61 | racecar is not a F1Car 62 | f1car is a Car 63 | f1car is a RaceCar 64 | f1car is a F1Car 65 | """ 66 | 67 | for class1 in car_classes: 68 | for class2 in car_classes: 69 | is_subclass = issubclass(class1, class2) 70 | msg = '{0} a subclass of'.format( 71 | 'is' if is_subclass else 'is not') 72 | print(class1.__name__, msg, class2.__name__) 73 | 74 | """ Prints: 75 | Car is a subclass of Car 76 | Car is not a subclass of RaceCar 77 | Car is not a subclass of F1Car 78 | RaceCar is a subclass of Car 79 | RaceCar is a subclass of RaceCar 80 | RaceCar is not a subclass of F1Car 81 | F1Car is a subclass of Car 82 | F1Car is a subclass of RaceCar 83 | F1Car is a subclass of F1Car 84 | """ 85 | -------------------------------------------------------------------------------- /ch12/pwdapi/core/handlers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | import falcon 5 | 6 | from .passwords import PasswordValidator, PasswordGenerator 7 | 8 | 9 | class HeaderMixin: 10 | def set_access_control_allow_origin(self, resp): 11 | resp.set_header('Access-Control-Allow-Origin', '*') 12 | 13 | 14 | class PasswordValidatorHandler(HeaderMixin): 15 | 16 | def on_post(self, req, resp): 17 | self.process_request(req, resp) 18 | 19 | password = req.context.get('_body', {}).get('password') 20 | if password is None: 21 | resp.status = falcon.HTTP_BAD_REQUEST 22 | return None 23 | 24 | result = self.parse_password(password) 25 | resp.body = json.dumps(result) 26 | 27 | def parse_password(self, password): 28 | validator = PasswordValidator(password) 29 | return { 30 | 'password': password, 31 | 'valid': validator.is_valid(), 32 | 'score': validator.score(), 33 | } 34 | 35 | def process_request(self, req, resp): 36 | self.set_access_control_allow_origin(resp) 37 | 38 | body = req.stream.read() 39 | if not body: 40 | raise falcon.HTTPBadRequest('Empty request body', 41 | 'A valid JSON document is required.') 42 | try: 43 | req.context['_body'] = json.loads( 44 | body.decode('utf-8')) 45 | except (ValueError, UnicodeDecodeError): 46 | raise falcon.HTTPError( 47 | falcon.HTTP_753, 'Malformed JSON', 48 | 'JSON incorrect or not utf-8 encoded.') 49 | 50 | 51 | class PasswordGeneratorHandler(HeaderMixin): 52 | 53 | def on_get(self, req, resp): 54 | self.process_request(req, resp) 55 | length = req.context.get('_length', 16) 56 | resp.body = json.dumps( 57 | PasswordGenerator.generate(length)) 58 | 59 | def process_request(self, req, resp): 60 | self.set_access_control_allow_origin(resp) 61 | length = req.get_param('length') 62 | if length is None: 63 | return 64 | try: 65 | length = int(length) 66 | assert length > 0 67 | req.context['_length'] = length 68 | except (ValueError, TypeError, AssertionError): 69 | raise falcon.HTTPBadRequest('Wrong query parameter', 70 | '`length` must be a positive integer.') 71 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/templates/records/record_add_edit.html: -------------------------------------------------------------------------------- 1 | {% extends "records/base.html" %} 2 | {% load static from staticfiles %} 3 | {% block title %} 4 | {% if update %}Update{% else %}Create{% endif %} Record 5 | {% endblock title %} 6 | 7 | {% block page-content %} 8 |

{% if update %}Update a{% else %}Create a new{% endif %} 9 | Record 10 |

11 | {% include "records/messages.html" %} 12 | 13 |
{% csrf_token %} 14 | {{ form.non_field_errors }} 15 | 16 |
{{ form.title.errors }} 17 | {{ form.title.label_tag }} {{ form.title }}
18 | 19 |
{{ form.username.errors }} 20 | {{ form.username.label_tag }} {{ form.username }}
21 | 22 |
{{ form.email.errors }} 23 | {{ form.email.label_tag }} {{ form.email }}
24 | 25 |
{{ form.url.errors }} 26 | {{ form.url.label_tag }} {{ form.url }}
27 | 28 |
{{ form.password.errors }} 29 | {{ form.password.label_tag }} {{ form.password }} 30 |
31 | 32 | 34 | 36 | 37 |
{{ form.notes.errors }} 38 | {{ form.notes.label_tag }} {{ form.notes }}
39 | 40 | 42 | 43 | 44 | 45 |
46 | {% endblock page-content %} 47 | 48 | {% block footer %} 49 |
{% include "records/footer.html" %}
50 | Go to the records list. 51 | {% endblock footer %} 52 | 53 | {% block scripts %} 54 | {{ block.super }} 55 | 56 | 57 | 73 | {% endblock scripts %} 74 | -------------------------------------------------------------------------------- /ch10/regex/entries/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re 3 | 4 | from django.contrib.auth.decorators import login_required 5 | from django.contrib.messages.views import SuccessMessageMixin 6 | from django.core.urlresolvers import reverse_lazy 7 | from django.utils.decorators import method_decorator 8 | from django.views.generic import FormView, TemplateView 9 | 10 | from .forms import EntryForm 11 | from .models import Entry 12 | 13 | 14 | class HomeView(TemplateView): 15 | template_name = 'entries/home.html' 16 | 17 | @method_decorator( 18 | login_required(login_url=reverse_lazy('login'))) 19 | def get(self, request, *args, **kwargs): 20 | return super(HomeView, self).get(request, *args, **kwargs) 21 | 22 | 23 | class EntryListView(TemplateView): 24 | template_name = 'entries/list.html' 25 | 26 | @method_decorator( 27 | login_required(login_url=reverse_lazy('login'))) 28 | def get(self, request, *args, **kwargs): 29 | context = self.get_context_data(**kwargs) 30 | entries = Entry.objects.filter( 31 | user=request.user).order_by('-date_added') 32 | matches = (self._parse_entry(entry) for entry in entries) 33 | context['entries'] = list(zip(entries, matches)) 34 | return self.render_to_response(context) 35 | 36 | def _parse_entry(self, entry): 37 | match = re.search(entry.pattern, entry.test_string) 38 | if match is not None: 39 | return ( 40 | match.group(), 41 | match.groups() or None, 42 | match.groupdict() or None 43 | ) 44 | return None 45 | 46 | 47 | class EntryFormView(SuccessMessageMixin, FormView): 48 | template_name = 'entries/insert.html' 49 | form_class = EntryForm 50 | success_url = reverse_lazy('insert') 51 | success_message = "Entry was created successfully" 52 | 53 | @method_decorator( 54 | login_required(login_url=reverse_lazy('login'))) 55 | def get(self, request, *args, **kwargs): 56 | return super(EntryFormView, self).get( 57 | request, *args, **kwargs) 58 | 59 | @method_decorator( 60 | login_required(login_url=reverse_lazy('login'))) 61 | def post(self, request, *args, **kwargs): 62 | return super(EntryFormView, self).post( 63 | request, *args, **kwargs) 64 | 65 | def form_valid(self, form): 66 | self._save_with_user(form) 67 | return super(EntryFormView, self).form_valid(form) 68 | 69 | def _save_with_user(self, form): 70 | self.object = form.save(commit=False) 71 | self.object.user = self.request.user 72 | self.object.save() 73 | -------------------------------------------------------------------------------- /ch12/pwdapi/core/passwords.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from math import ceil 3 | from random import sample 4 | from string import ascii_lowercase, ascii_uppercase, digits 5 | 6 | 7 | punctuation = '!#$%&()*+-?@_|' 8 | allchars = ''.join( 9 | (ascii_lowercase, ascii_uppercase, digits, punctuation)) 10 | 11 | 12 | class PasswordValidator: 13 | 14 | def __init__(self, password): 15 | self.password = password.strip() 16 | 17 | def is_valid(self): 18 | return (len(self.password) > 0 and 19 | all(char in allchars for char in self.password)) 20 | 21 | def score(self): 22 | result = { 23 | 'length': self._score_length(), 24 | 'case': self._score_case(), 25 | 'numbers': self._score_numbers(), 26 | 'special': self._score_special(), 27 | 'ratio': self._score_ratio(), 28 | } 29 | result['total'] = sum(result.values()) 30 | return result 31 | 32 | def _score_length(self): 33 | scores_list = ([0]*4) + ([1]*4) + ([3]*4) + ([5]*4) 34 | scores = dict(enumerate(scores_list)) 35 | return scores.get(len(self.password), 7) 36 | 37 | def _score_case(self): 38 | lower = bool(set(ascii_lowercase) & set(self.password)) 39 | upper = bool(set(ascii_uppercase) & set(self.password)) 40 | return int(lower or upper) + 2 * (lower and upper) 41 | 42 | def _score_numbers(self): 43 | return 2 if (set(self.password) & set(digits)) else 0 44 | 45 | def _score_special(self): 46 | return 4 if ( 47 | set(self.password) & set(punctuation)) else 0 48 | 49 | def _score_ratio(self): 50 | alpha_count = sum( 51 | 1 if c.lower() in ascii_lowercase else 0 52 | for c in self.password) 53 | digits_count = sum( 54 | 1 if c in digits else 0 for c in self.password) 55 | if digits_count == 0: 56 | return 0 57 | return min(ceil(alpha_count / digits_count), 7) 58 | 59 | 60 | class PasswordGenerator: 61 | 62 | @classmethod 63 | def generate(cls, length, bestof=10): 64 | candidates = sorted([ 65 | cls._generate_candidate(length) 66 | for k in range(max(1, bestof)) 67 | ]) 68 | return candidates[-1] 69 | 70 | @classmethod 71 | def _generate_candidate(cls, length): 72 | password = cls._generate_password(length) 73 | score = PasswordValidator(password).score() 74 | return (score['total'], password) 75 | 76 | @classmethod 77 | def _generate_password(cls, length): 78 | chars = allchars * (ceil(length / len(allchars))) 79 | return ''.join(sample(chars, length)) 80 | -------------------------------------------------------------------------------- /ch2/lists.py: -------------------------------------------------------------------------------- 1 | # lists.py 2 | 3 | 4 | # creation 5 | >>> [] # empty list 6 | [] 7 | >>> list() # same as [] 8 | [] 9 | >>> [1, 2, 3] # as with tuples, items are comma separated 10 | [1, 2, 3] 11 | >>> [x + 5 for x in [2, 3, 4]] # Python is magic 12 | [7, 8, 9] 13 | >>> list((1, 3, 5, 7, 9)) # list from a tuple 14 | [1, 3, 5, 7, 9] 15 | >>> list('hello') # list from a string 16 | ['h', 'e', 'l', 'l', 'o'] 17 | 18 | 19 | # main methods 20 | >>> a = [1, 2, 1, 3] 21 | >>> a.append(13) # we can append anything at the end 22 | >>> a 23 | [1, 2, 1, 3, 13] 24 | >>> a.count(1) # how many `1` are there in the list? 25 | 2 26 | >>> a.extend([5, 7]) # extend the list by another (or sequence) 27 | >>> a 28 | [1, 2, 1, 3, 13, 5, 7] 29 | >>> a.index(13) # position of `13` in the list (0-based indexing) 30 | 4 31 | >>> a.insert(0, 17) # insert `17` at position 0 32 | >>> a 33 | [17, 1, 2, 1, 3, 13, 5, 7] 34 | >>> a.pop() # pop (remove and return) last element 35 | 7 36 | >>> a.pop(3) # pop element at position 3 37 | 1 38 | >>> a 39 | [17, 1, 2, 3, 13, 5] 40 | >>> a.remove(17) # remove `17` from the list 41 | >>> a 42 | [1, 2, 3, 13, 5] 43 | >>> a.reverse() # reverse the order of the elements in the list 44 | >>> a 45 | [5, 13, 3, 2, 1] 46 | >>> a.sort() # sort the list 47 | >>> a 48 | [1, 2, 3, 5, 13] 49 | >>> a.clear() # remove all elements from the list 50 | >>> a 51 | [] 52 | 53 | 54 | # extending 55 | >>> a = list('hello') # makes a list from a string 56 | >>> a 57 | ['h', 'e', 'l', 'l', 'o'] 58 | >>> a.append(100) # append 100, heterogeneous type 59 | >>> a 60 | ['h', 'e', 'l', 'l', 'o', 100] 61 | >>> a.extend((1, 2, 3)) # extend using tuple 62 | >>> a 63 | ['h', 'e', 'l', 'l', 'o', 100, 1, 2, 3] 64 | >>> a.extend('...') # extend using string 65 | >>> a 66 | ['h', 'e', 'l', 'l', 'o', 100, 1, 2, 3, '.', '.', '.'] 67 | 68 | 69 | # most common operations 70 | >>> a = [1, 3, 5, 7] 71 | >>> min(a) # minimum value in the list 72 | 1 73 | >>> max(a) # maximum value in the list 74 | 7 75 | >>> sum(a) # sum of all values in the list 76 | 16 77 | >>> len(a) # number of elements in the list 78 | 4 79 | >>> b = [6, 7, 8] 80 | >>> a + b # `+` with list means concatenation 81 | [1, 3, 5, 7, 6, 7, 8] 82 | >>> a * 2 # `*` has also a special meaning 83 | [1, 3, 5, 7, 1, 3, 5, 7] 84 | 85 | 86 | # cool sorting 87 | >>> from operator import itemgetter 88 | >>> a = [(5, 3), (1, 3), (1, 2), (2, -1), (4, 9)] 89 | >>> sorted(a) 90 | [(1, 2), (1, 3), (2, -1), (4, 9), (5, 3)] 91 | >>> sorted(a, key=itemgetter(0)) 92 | [(1, 3), (1, 2), (2, -1), (4, 9), (5, 3)] 93 | >>> sorted(a, key=itemgetter(0, 1)) 94 | [(1, 2), (1, 3), (2, -1), (4, 9), (5, 3)] 95 | >>> sorted(a, key=itemgetter(1)) 96 | [(2, -1), (1, 2), (5, 3), (1, 3), (4, 9)] 97 | >>> sorted(a, key=itemgetter(1), reverse=True) 98 | [(4, 9), (5, 3), (1, 3), (1, 2), (2, -1)] 99 | -------------------------------------------------------------------------------- /ch12/pwdweb/records/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.contrib import messages 3 | from django.contrib.messages.views import SuccessMessageMixin 4 | from django.core.urlresolvers import reverse_lazy 5 | from django.views.generic import TemplateView 6 | from django.views.generic.edit import ( 7 | CreateView, UpdateView, DeleteView) 8 | 9 | from .forms import RecordForm 10 | from .models import Record 11 | 12 | 13 | class HomeView(TemplateView): 14 | template_name = 'records/home.html' 15 | 16 | 17 | class EncryptionMixin: 18 | 19 | def form_valid(self, form): 20 | self.encrypt_password(form) 21 | return super(EncryptionMixin, self).form_valid(form) 22 | 23 | def encrypt_password(self, form): 24 | self.object = form.save(commit=False) 25 | self.object.encrypt_password() 26 | self.object.save() 27 | 28 | 29 | class RecordCreateView( 30 | EncryptionMixin, SuccessMessageMixin, CreateView): 31 | template_name = 'records/record_add_edit.html' 32 | form_class = RecordForm 33 | success_url = reverse_lazy('records:add') 34 | success_message = 'Record was created successfully' 35 | 36 | 37 | class RecordUpdateView( 38 | EncryptionMixin, SuccessMessageMixin, UpdateView): 39 | template_name = 'records/record_add_edit.html' 40 | form_class = RecordForm 41 | model = Record 42 | success_message = 'Record was updated successfully' 43 | 44 | def get_context_data(self, **kwargs): 45 | kwargs['update'] = True 46 | return super( 47 | RecordUpdateView, self).get_context_data(**kwargs) 48 | 49 | def form_valid(self, form): 50 | self.success_url = reverse_lazy( 51 | 'records:edit', 52 | kwargs={'pk': self.object.pk} 53 | ) 54 | return super(RecordUpdateView, self).form_valid(form) 55 | 56 | def get_form_kwargs(self): 57 | kwargs = super(RecordUpdateView, self).get_form_kwargs() 58 | kwargs['instance'].decrypt_password() 59 | return kwargs 60 | 61 | 62 | class RecordDeleteView(SuccessMessageMixin, DeleteView): 63 | model = Record 64 | success_url = reverse_lazy('records:list') 65 | 66 | def delete(self, request, *args, **kwargs): 67 | messages.success( 68 | request, 'Record was deleted successfully') 69 | return super(RecordDeleteView, self).delete( 70 | request, *args, **kwargs) 71 | 72 | 73 | class RecordListView(TemplateView): 74 | template_name = 'records/list.html' 75 | 76 | def get(self, request, *args, **kwargs): 77 | context = self.get_context_data(**kwargs) 78 | records = Record.objects.all().order_by('title') #1 79 | for record in records: 80 | record.plaintext = record.decrypt(record.password) #2 81 | context['records'] = records 82 | return self.render_to_response(context) 83 | -------------------------------------------------------------------------------- /ch2/dicts.py: -------------------------------------------------------------------------------- 1 | # dicts.py 2 | 3 | 4 | >>> a = dict(A=1, Z=-1) 5 | >>> b = {'A': 1, 'Z': -1} 6 | >>> c = dict(zip(['A', 'Z'], [1, -1])) 7 | >>> d = dict([('A', 1), ('Z', -1)]) 8 | >>> e = dict({'Z': -1, 'A': 1}) 9 | >>> a == b == c == d == e # are they all the same? 10 | True # indeed! 11 | 12 | 13 | # zip 14 | >>> list(zip(['h', 'e', 'l', 'l', 'o'], [1, 2, 3, 4, 5])) 15 | [('h', 1), ('e', 2), ('l', 3), ('l', 4), ('o', 5)] 16 | >>> list(zip('hello', range(1, 6))) # equivalent, more pythonic 17 | [('h', 1), ('e', 2), ('l', 3), ('l', 4), ('o', 5)] 18 | 19 | 20 | # basic 21 | >>> d = {} 22 | >>> d['a'] = 1 # let's set a couple of (key, value) pairs 23 | >>> d['b'] = 2 24 | >>> len(d) # how many pairs? 25 | 2 26 | >>> d['a'] # what is the value of 'a'? 27 | 1 28 | >>> d # how does `d` look now? 29 | {'a': 1, 'b': 2} 30 | >>> del d['a'] # let's remove `a` 31 | >>> d 32 | {'b': 2} 33 | >>> d['c'] = 3 # let's add 'c': 3 34 | >>> 'c' in d # membership is checked against the keys 35 | True 36 | >>> 3 in d # not the values 37 | False 38 | >>> 'e' in d 39 | False 40 | >>> d.clear() # let's clean everything from this dictionary 41 | >>> d 42 | {} 43 | 44 | 45 | # views 46 | >>> d = dict(zip('hello', range(5))) 47 | >>> d 48 | {'e': 1, 'h': 0, 'o': 4, 'l': 3} 49 | >>> d.keys() 50 | dict_keys(['e', 'h', 'o', 'l']) 51 | >>> d.values() 52 | dict_values([1, 0, 4, 3]) 53 | >>> d.items() 54 | dict_items([('e', 1), ('h', 0), ('o', 4), ('l', 3)]) 55 | >>> 3 in d.values() 56 | True 57 | >>> ('o', 4) in d.items() 58 | True 59 | 60 | 61 | # other methods 62 | >>> d 63 | {'e': 1, 'h': 0, 'o': 4, 'l': 3} 64 | >>> d.popitem() # removes a random item (useful in algorithms) 65 | ('e', 1) 66 | >>> d 67 | {'h': 0, 'o': 4, 'l': 3} 68 | >>> d.pop('l') # remove item with key `l` 69 | 3 70 | >>> d.pop('not-a-key') # remove a key not in dictionary: KeyError 71 | Traceback (most recent call last): 72 | File "", line 1, in 73 | KeyError: 'not-a-key' 74 | >>> d.pop('not-a-key', 'default-value') # with a default value? 75 | 'default-value' # we get the default value 76 | >>> d.update({'another': 'value'}) # we can update dict this way 77 | >>> d.update(a=13) # or this way (like a function call) 78 | >>> d 79 | {'a': 13, 'another': 'value', 'h': 0, 'o': 4} 80 | >>> d.get('a') # same as d['a'] but if key is missing no KeyError 81 | 13 82 | >>> d.get('a', 177) # default value used if key is missing 83 | 13 84 | >>> d.get('b', 177) # like in this case 85 | 177 86 | >>> d.get('b') # key is not there, so None is returned 87 | 88 | 89 | # setdefault 90 | >>> d = {} 91 | >>> d.setdefault('a', 1) # 'a' is missing, we get default value 92 | 1 93 | >>> d 94 | {'a': 1} # also, the key/value pair ('a', 1) has now been added 95 | >>> d.setdefault('a', 5) # let's try to override the value 96 | 1 97 | >>> d 98 | {'a': 1} # didn't work, as expected 99 | 100 | 101 | # setdefault example 102 | >>> d = {} 103 | >>> d.setdefault('a', {}).setdefault('b', []).append(1) 104 | >>> d 105 | {'a': {'b': [1]}} 106 | -------------------------------------------------------------------------------- /ch7/profiling/triples.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | def calc_triples(mx): 5 | triples = [] 6 | for a in range(1, mx + 1): 7 | for b in range(a, mx + 1): 8 | hypotenuse = calc_hypotenuse(a, b) 9 | if is_int(hypotenuse): 10 | triples.append((a, b, int(hypotenuse))) 11 | return triples 12 | 13 | 14 | def calc_hypotenuse(a, b): 15 | return (a*a + b*b) ** .5 16 | 17 | 18 | def is_int(n): # n is expected to be a float 19 | return n.is_integer() 20 | 21 | 22 | triples = calc_triples(1000) 23 | 24 | """ 25 | def calc_hypotenuse(a, b): 26 | return (a**2 + b**2) ** .5 27 | 28 | 29 | $ python -m cProfile triples.py 30 | 1502538 function calls in 0.750 seconds 31 | 32 | Ordered by: standard name 33 | 34 | ncalls tottime percall filename:lineno(function) 35 | 500500 0.469 0.000 triples.py:14(calc_hypotenuse) 36 | 500500 0.087 0.000 triples.py:18(is_int) 37 | 1 0.000 0.000 triples.py:4() 38 | 1 0.163 0.163 triples.py:4(calc_triples) 39 | 1 0.000 0.000 {built-in method exec} 40 | 1034 0.000 0.000 {method 'append' of 'list' objects} 41 | 1 0.000 0.000 {method 'disable' of '_lsprof.Profil... 42 | 500500 0.032 0.000 {method 'is_integer' of 'float' objects} 43 | """ 44 | 45 | 46 | """ 47 | def calc_hypotenuse(a, b): 48 | return (a*a + b*b) ** .5 49 | 50 | 51 | $ python -m cProfile triples.py 52 | 1502538 function calls in 0.447 seconds 53 | 54 | Ordered by: standard name 55 | 56 | ncalls tottime percall cumtime percall filename:lineno(function) 57 | 500500 0.179 0.000 0.179 0.000 triples.py:14(calc_hypotenuse) 58 | 500500 0.084 0.000 0.113 0.000 triples.py:18(is_int) 59 | 1 0.000 0.000 0.447 0.447 triples.py:4() 60 | 1 0.155 0.155 0.447 0.447 triples.py:4(calc_triples) 61 | 1 0.000 0.000 0.447 0.447 {built-in method exec} 62 | 1034 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 63 | 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 64 | 500500 0.029 0.000 0.029 0.000 {method 'is_integer' of 'float' objects} 65 | """ 66 | 67 | 68 | """ 69 | def is_int(n): 70 | return n == int(n) 71 | 72 | 73 | $ python -m cProfile triples.py 74 | 1002038 function calls in 0.466 seconds 75 | 76 | Ordered by: standard name 77 | 78 | ncalls tottime percall cumtime percall filename:lineno(function) 79 | 500500 0.173 0.000 0.173 0.000 triples.py:14(calc_hypotenuse) 80 | 500500 0.141 0.000 0.141 0.000 triples.py:18(is_int) 81 | 1 0.000 0.000 0.466 0.466 triples.py:4() 82 | 1 0.151 0.151 0.466 0.466 triples.py:4(calc_triples) 83 | 1 0.000 0.000 0.466 0.466 {built-in method exec} 84 | 1034 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 85 | 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 86 | """ 87 | -------------------------------------------------------------------------------- /ch8/scrape.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import base64 4 | import json 5 | import os 6 | 7 | from bs4 import BeautifulSoup 8 | import requests 9 | 10 | 11 | def scrape(url, format_, type_): 12 | try: 13 | page = requests.get(url) 14 | except requests.RequestException as rex: 15 | print(str(rex)) 16 | else: 17 | soup = BeautifulSoup(page.content, 'html.parser') 18 | images = _fetch_images(soup, url) 19 | images = _filter_images(images, type_) 20 | _save(images, format_) 21 | 22 | 23 | def _fetch_images(soup, base_url): 24 | # Works only with relative src paths. 25 | images = [] 26 | for img in soup.findAll('img'): 27 | src = img.get('src') 28 | img_url = ( 29 | '{base_url}/{src}'.format( 30 | base_url=base_url, src=src)) 31 | name = img_url.split('/')[-1] 32 | images.append(dict(name=name, url=img_url)) 33 | return images 34 | 35 | 36 | def _filter_images(images, type_): 37 | if type_ == 'all': 38 | return images 39 | ext_map = { 40 | 'png': ['.png'], 41 | 'jpg': ['.jpg', '.jpeg'], 42 | } 43 | return [ 44 | img for img in images 45 | if _matches_extension(img['name'], ext_map[type_]) 46 | ] 47 | 48 | 49 | def _matches_extension(filename, extension_list): 50 | name, extension = os.path.splitext(filename.lower()) 51 | return extension in extension_list 52 | 53 | 54 | def _save(images, format_): 55 | if images: 56 | if format_ == 'img': 57 | _save_images(images) 58 | else: 59 | _save_json(images) 60 | print('Done') 61 | else: 62 | print('No images to save.') 63 | 64 | 65 | def _save_images(images): 66 | for img in images: 67 | img_data = requests.get(img['url']).content 68 | with open(img['name'], 'wb') as f: 69 | f.write(img_data) 70 | 71 | 72 | def _save_json(images): 73 | data = {} 74 | for img in images: 75 | img_data = requests.get(img['url']).content 76 | b64_img_data = base64.b64encode(img_data) 77 | str_img_data = b64_img_data.decode('utf-8') 78 | data[img['name']] = str_img_data 79 | 80 | with open('images.json', 'w') as ijson: 81 | ijson.write(json.dumps(data)) 82 | 83 | 84 | if __name__ == "__main__": 85 | 86 | parser = argparse.ArgumentParser( 87 | description='Scrape a webpage.') 88 | parser.add_argument( 89 | '-t', 90 | '--type', 91 | choices=['all', 'png', 'jpg'], 92 | default='all', 93 | help='The image type we want to scrape.') 94 | 95 | parser.add_argument( 96 | '-f', 97 | '--format', 98 | choices=['img', 'json'], 99 | default='img', 100 | help='The format images are _saved to.') 101 | 102 | parser.add_argument( 103 | 'url', 104 | help='The URL we want to scrape for images.') 105 | 106 | args = parser.parse_args() 107 | scrape(args.url, args.format, args.type) 108 | -------------------------------------------------------------------------------- /ch10/regex/regex/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for regex project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'qir5y@j((v#9t2py1@u@@gn#=32fw90*4&li-qm-%1v1u_!po+' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'entries', 41 | ) 42 | 43 | MIDDLEWARE_CLASSES = ( 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | 'django.middleware.security.SecurityMiddleware', 52 | ) 53 | 54 | ROOT_URLCONF = 'regex.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'regex.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.sqlite3', 81 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 82 | } 83 | } 84 | 85 | 86 | # Internationalization 87 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 88 | 89 | LANGUAGE_CODE = 'en-gb' 90 | 91 | TIME_ZONE = 'Europe/London' 92 | 93 | USE_I18N = True 94 | 95 | USE_L10N = True 96 | 97 | USE_TZ = True 98 | 99 | 100 | # Static files (CSS, JavaScript, Images) 101 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 102 | 103 | STATIC_URL = '/static/' 104 | -------------------------------------------------------------------------------- /ch12/pwdweb/pwdweb/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for pwdweb project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '=g!!xgz0ywz@#@&c@qc&$u!!_iv@kw1&q+92f(v6^_fime_f$3' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'records', 41 | # 'debug_toolbar', 42 | # 'django_extensions', 43 | ) 44 | 45 | MIDDLEWARE_CLASSES = ( 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 51 | 'django.contrib.messages.middleware.MessageMiddleware', 52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 53 | 'django.middleware.security.SecurityMiddleware', 54 | ) 55 | 56 | ROOT_URLCONF = 'pwdweb.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'pwdweb.wsgi.application' 75 | 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 79 | 80 | DATABASES = { 81 | 'default': { 82 | 'ENGINE': 'django.db.backends.sqlite3', 83 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 84 | } 85 | } 86 | 87 | 88 | # Internationalization 89 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 90 | 91 | LANGUAGE_CODE = 'en-gb' 92 | 93 | TIME_ZONE = 'Europe/London' 94 | 95 | USE_I18N = True 96 | 97 | USE_L10N = True 98 | 99 | USE_TZ = True 100 | 101 | 102 | # Static files (CSS, JavaScript, Images) 103 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 104 | 105 | STATIC_URL = '/static/' 106 | 107 | # SECURITY WARNING: Keep this key secret on production! 108 | ENCRYPTION_KEY = b'qMhPGx-ROWUDr4veh0ybPRL6viIUNe0vcPDmy67x6CQ=' 109 | -------------------------------------------------------------------------------- /requirements/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file requirements.txt requirements.in 6 | # 7 | appdirs==1.4.2 # via setuptools 8 | appnope==0.1.0 # via ipython 9 | babel==2.3.4 # via delorean 10 | backports-abc==0.5 # via tornado 11 | beautifulsoup4==4.4.0 12 | bleach==1.5.0 # via nbconvert 13 | cffi==1.9.1 # via cryptography 14 | cryptography==1.7.1 15 | cycler==0.10.0 # via matplotlib 16 | decorator==4.0.11 # via ipython, traitlets 17 | delorean==0.6.0 18 | django==1.8.17 19 | entrypoints==0.2.2 # via nbconvert 20 | faker==0.7.7 21 | falcon==0.3.0 22 | flask==0.10.1 23 | gunicorn==19.3.0 24 | html5lib==0.9999999 # via bleach 25 | humanize==0.5.1 # via delorean 26 | idna==2.2 # via cryptography 27 | ipykernel==4.5.2 # via ipywidgets, jupyter, jupyter-console, notebook, qtconsole 28 | ipython-genutils==0.1.0 # via nbformat, notebook, traitlets 29 | ipython==5.1.0 30 | ipywidgets==5.2.2 # via jupyter 31 | itsdangerous==0.24 # via flask 32 | jinja2==2.9.5 # via flask, nbconvert, notebook 33 | jsonschema==2.6.0 # via nbformat 34 | jupyter-client==5.0.0 # via ipykernel, jupyter-console, notebook, qtconsole 35 | jupyter-console==5.1.0 # via jupyter 36 | jupyter-core==4.3.0 # via jupyter-client, nbconvert, nbformat, notebook, qtconsole 37 | jupyter==1.0.0 38 | markupsafe==0.23 # via jinja2 39 | matplotlib==1.5.3 40 | mistune==0.7.3 # via nbconvert 41 | nbconvert==5.1.1 # via jupyter, notebook 42 | nbformat==4.3.0 # via nbconvert, notebook 43 | notebook==4.4.1 # via jupyter, widgetsnbextension 44 | numpy==1.11.3 45 | packaging==16.8 # via setuptools 46 | pandas==0.19.2 47 | pandocfilters==1.4.1 # via nbconvert 48 | pep8==1.6.2 49 | pexpect==4.2.1 # via ipython 50 | pickleshare==0.7.4 # via ipython 51 | prompt-toolkit==1.0.13 # via ipython, jupyter-console 52 | ptyprocess==0.5.1 # via pexpect, terminado 53 | pyasn1==0.2.3 # via cryptography 54 | pycparser==2.17 # via cffi 55 | pygments==2.2.0 # via ipython, jupyter-console, nbconvert, qtconsole 56 | pyparsing==2.1.10 # via matplotlib, packaging 57 | python-dateutil==2.6.0 # via delorean, faker, jupyter-client, matplotlib, pandas 58 | python-mimeparse==1.6.0 # via falcon 59 | pytz==2016.10 # via babel, delorean, matplotlib, pandas, tzlocal 60 | pyzmq==16.0.2 # via jupyter-client 61 | qtconsole==4.2.1 # via jupyter 62 | requests==2.7.0 63 | simplegeneric==0.8.1 # via ipython 64 | six==1.10.0 # via bleach, cryptography, cycler, faker, falcon, html5lib, packaging, prompt-toolkit, python-dateutil, setuptools, traitlets 65 | terminado==0.6 # via notebook 66 | testpath==0.3 # via nbconvert 67 | tornado==4.4.2 # via ipykernel, notebook, terminado 68 | traitlets==4.3.2 # via ipykernel, ipython, ipywidgets, jupyter-client, jupyter-core, nbconvert, nbformat, notebook, qtconsole 69 | tzlocal==1.3 # via delorean 70 | wcwidth==0.1.7 # via prompt-toolkit 71 | werkzeug==0.11.15 # via flask 72 | widgetsnbextension==1.2.6 # via ipywidgets 73 | xlwt==1.1.2 74 | 75 | # The following packages are considered to be unsafe in a requirements file: 76 | # setuptools # via cryptography, ipython 77 | -------------------------------------------------------------------------------- /ch2/numbers.py: -------------------------------------------------------------------------------- 1 | # numbers.py 2 | 3 | 4 | # integers 5 | >>> a = 12 6 | >>> b = 3 7 | >>> a + b # addition 8 | 15 9 | >>> b - a # subtraction 10 | -9 11 | >>> a // b # integer division 12 | 4 13 | >>> a / b # true division 14 | 4.0 15 | >>> 10 % 3 # remainder of 10 / 3 16 | 1 17 | >>> a * b # multiplication 18 | 36 19 | >>> b ** a # power operator 20 | 531441 21 | >>> 2 ** 1024 # a very big number, Python handles it gracefully 22 | 17976931348623159077293051907890247336179769789423065727343008115 23 | 77326758055009631327084773224075360211201138798713933576587897688 24 | 14416622492847430639474124377767893424865485276302219601246094119 25 | 45308295208500576883815068234246288147391311054082723716335051068 26 | 4586298239947245938479716304835356329624224137216 27 | 28 | 29 | # integer and true division 30 | >>> 7 / 4 # true division 31 | 1.75 32 | >>> 7 // 4 # integer division, truncation returns 1 33 | 1 34 | >>> -7 / 4 # true division again, result is opposite of previous 35 | -1.75 36 | >>> -7 // 4 # integer div., result not the opposite of previous 37 | -2 38 | 39 | # modulo operator 40 | >>> 10 % 3 # remainder of the division 10 // 3 41 | 1 42 | >>> 10 % 4 # remainder of the division 10 // 4 43 | 2 44 | 45 | 46 | # truncation towards 0 47 | >>> int(1.75) 48 | 1 49 | >>> int(-1.75) 50 | -1 51 | 52 | 53 | # booleans 54 | >>> int(True) # True behaves like 1 55 | 1 56 | >>> int(False) # False behaves like 0 57 | 0 58 | >>> bool(1) # 1 evaluates to True in a boolean context 59 | True 60 | >>> bool(-42) # and so does every non-zero number 61 | True 62 | >>> bool(0) # 0 evaluates to False 63 | False 64 | >>> # quick peak at the operators (and, or, not) 65 | >>> not True 66 | False 67 | >>> not False 68 | True 69 | >>> True and True 70 | True 71 | >>> False or True 72 | True 73 | 74 | 75 | # reals 76 | >>> pi = 3.1415926536 # how many digits of PI can you remember? 77 | >>> radius = 4.5 78 | >>> area = pi * (radius ** 2) 79 | >>> area 80 | 63.61725123519331 81 | 82 | 83 | # sys.float_info 84 | >>> import sys 85 | >>> sys.float_info 86 | sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1) 87 | 88 | 89 | # approximation issue 90 | >>> 3 * 0.1 - 0.3 # this should be 0!!! 91 | 5.551115123125783e-17 92 | 93 | 94 | # complex 95 | >>> c = 3.14 + 2.73j 96 | >>> c.real # real part 97 | 3.14 98 | >>> c.imag # imaginary part 99 | 2.73 100 | >>> c.conjugate() # conjugate of A + Bj is A - Bj 101 | (3.14-2.73j) 102 | >>> c * 2 # multiplication is allowed 103 | (6.28+5.46j) 104 | >>> c ** 2 # power operation as well 105 | (2.4067000000000007+17.1444j) 106 | >>> d = 1 + 1j # addition and subtraction as well 107 | >>> c - d 108 | (2.14+1.73j) 109 | 110 | 111 | # fractions 112 | >>> from fractions import Fraction 113 | >>> Fraction(10, 6) # mad hatter? 114 | Fraction(5, 3) # notice it's been reduced to lowest terms 115 | >>> Fraction(1, 3) + Fraction(2, 3) # 1/3 + 2/3 == 3/3 == 1/1 116 | Fraction(1, 1) 117 | >>> f = Fraction(10, 6) 118 | >>> f.numerator 119 | 5 120 | >>> f.denominator 121 | 3 122 | 123 | 124 | # decimal 125 | >>> from decimal import Decimal as D # rename for brevity 126 | >>> D(3.14) # pi, from float, so approximation issues 127 | Decimal('3.140000000000000124344978758017532527446746826171875') 128 | >>> D('3.14') # pi, from a string, so no approximation issues 129 | Decimal('3.14') 130 | >>> D(0.1) * D(3) - D(0.3) # from float, we still have the issue 131 | Decimal('2.775557561565156540423631668E-17') 132 | >>> D('0.1') * D(3) - D('0.3') # from string, all perfect 133 | Decimal('0.0') 134 | -------------------------------------------------------------------------------- /ch12/pwdapi/tests/test_core/test_handlers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | from unittest.mock import patch 4 | 5 | from nose.tools import assert_dict_equal, assert_equal 6 | 7 | import falcon 8 | import falcon.testing as testing 9 | 10 | from core.handlers import ( 11 | PasswordValidatorHandler, PasswordGeneratorHandler) 12 | 13 | 14 | class PGHTest(PasswordGeneratorHandler): 15 | def process_request(self, req, resp): 16 | self.req, self.resp = req, resp 17 | return super(PGHTest, self).process_request(req, resp) 18 | 19 | 20 | class PVHTest(PasswordValidatorHandler): 21 | def process_request(self, req, resp): 22 | self.req, self.resp = req, resp 23 | return super(PVHTest, self).process_request(req, resp) 24 | 25 | 26 | class TestPasswordValidatorHandler(testing.TestBase): 27 | 28 | def before(self): 29 | self.resource = PVHTest() 30 | self.api.add_route('/password/validate/', self.resource) 31 | 32 | def test_post(self): 33 | self.simulate_request( 34 | '/password/validate/', 35 | body=json.dumps({'password': 'abcABC0123#&'}), 36 | method='POST') 37 | resp = self.resource.resp 38 | 39 | assert_equal('200 OK', resp.status) 40 | assert_dict_equal( 41 | {'password': 'abcABC0123#&', 42 | 'score': {'case': 3, 'length': 5, 'numbers': 2, 43 | 'special': 4, 'ratio': 2, 'total': 16}, 44 | 'valid': True}, 45 | json.loads(resp.body)) 46 | 47 | def test_post_empty_body(self): 48 | self.simulate_request( 49 | '/password/validate/', 50 | method='POST') 51 | assert_equal('400 Bad Request', self.srmock.status) 52 | 53 | def test_post_malformed_json(self): 54 | self.simulate_request( 55 | '/password/validate/', 56 | body='a', 57 | method='POST') 58 | assert_equal('753 Syntax Error', self.srmock.status) 59 | 60 | 61 | class TestPasswordGeneratorHandler(testing.TestBase): 62 | 63 | def before(self): 64 | self.resource = PGHTest() 65 | self.api.add_route('/password/generate/', self.resource) 66 | 67 | @patch('core.handlers.PasswordGenerator') 68 | def test_get(self, PasswordGenerator): 69 | PasswordGenerator.generate.return_value = (7, 'abc123') 70 | self.simulate_request( 71 | '/password/generate/', 72 | query_string='length=7', 73 | method='GET') 74 | resp = self.resource.resp 75 | 76 | assert_equal('200 OK', resp.status) 77 | assert_equal([7, 'abc123'], json.loads(resp.body)) 78 | 79 | def test_get_no_length(self): 80 | self.simulate_request( 81 | '/password/generate/', method='GET') 82 | resp = self.resource.resp 83 | 84 | assert_equal('200 OK', resp.status) 85 | # default length when not provided 86 | assert_equal(16, len(json.loads(resp.body)[1])) 87 | 88 | def test_get_length(self): 89 | self.simulate_request( 90 | '/password/generate/', 91 | query_string='length=42', 92 | method='GET') 93 | resp = self.resource.resp 94 | 95 | assert_equal('200 OK', resp.status) 96 | assert_equal(42, len(json.loads(resp.body)[1])) 97 | 98 | def test_get_length_not_int(self): 99 | self.simulate_request( 100 | '/password/generate/', 101 | query_string='length=not-an-int', 102 | method='GET') 103 | resp = self.resource.resp 104 | 105 | assert_equal('400 Bad Request', resp.status) 106 | 107 | def test_get_length_integer_not_positive(self): 108 | self.simulate_request( 109 | '/password/generate/', 110 | query_string='length=-42', 111 | method='GET') 112 | resp = self.resource.resp 113 | 114 | assert_equal('400 Bad Request', resp.status) 115 | -------------------------------------------------------------------------------- /ch8/guiscrape.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tkinter import * 3 | from tkinter import ttk, filedialog, messagebox 4 | import base64 5 | import json 6 | import os 7 | 8 | from bs4 import BeautifulSoup 9 | import requests 10 | 11 | 12 | config = {} 13 | 14 | 15 | def fetch_url(): 16 | url = _url.get() 17 | config['images'] = [] 18 | _images.set(()) # initialised as an empty tuple 19 | try: 20 | page = requests.get(url) 21 | except requests.RequestException as rex: 22 | _sb(str(rex)) 23 | else: 24 | soup = BeautifulSoup(page.content, 'html.parser') 25 | images = fetch_images(soup, url) 26 | if images: 27 | _images.set(tuple(img['name'] for img in images)) 28 | _sb('Images found: {}'.format(len(images))) 29 | else: 30 | _sb('No images found') 31 | config['images'] = images 32 | 33 | 34 | def fetch_images(soup, base_url): 35 | images = [] 36 | for img in soup.findAll('img'): 37 | src = img.get('src') 38 | img_url = ( 39 | '{base_url}/{src}'.format(base_url=base_url, src=src)) 40 | name = img_url.split('/')[-1] 41 | images.append(dict(name=name, url=img_url)) 42 | return images 43 | 44 | 45 | def save(): 46 | if not config.get('images'): 47 | _alert('No images to save') 48 | return 49 | 50 | if _save_method.get() == 'img': 51 | dirname = filedialog.askdirectory(mustexist=True) 52 | _save_images(dirname) 53 | else: 54 | filename = filedialog.asksaveasfilename( 55 | initialfile='images.json', 56 | filetypes=[('JSON', '.json')]) 57 | _save_json(filename) 58 | 59 | 60 | def _save_images(dirname): 61 | if dirname and config.get('images'): 62 | for img in config['images']: 63 | img_data = requests.get(img['url']).content 64 | filename = os.path.join(dirname, img['name']) 65 | with open(filename, 'wb') as f: 66 | f.write(img_data) 67 | _alert('Done') 68 | 69 | 70 | def _save_json(filename): 71 | if filename and config.get('images'): 72 | data = {} 73 | for img in config['images']: 74 | img_data = requests.get(img['url']).content 75 | b64_img_data = base64.b64encode(img_data) 76 | str_img_data = b64_img_data.decode('utf-8') 77 | data[img['name']] = str_img_data 78 | 79 | with open(filename, 'w') as ijson: 80 | ijson.write(json.dumps(data)) 81 | _alert('Done') 82 | 83 | 84 | def _sb(msg): 85 | _status_msg.set(msg) 86 | 87 | 88 | def _alert(msg): 89 | messagebox.showinfo(message=msg) 90 | 91 | 92 | if __name__ == "__main__": 93 | 94 | _root = Tk() 95 | _root.title('Scrape app') 96 | 97 | _mainframe = ttk.Frame(_root, padding='5 5 5 5') 98 | _mainframe.grid(row=0, column=0, sticky=(E, W, N, S)) 99 | 100 | _url_frame = ttk.LabelFrame( 101 | _mainframe, text='URL', padding='5 5 5 5') 102 | _url_frame.grid(row=0, column=0, sticky=(E, W)) 103 | _url_frame.columnconfigure(0, weight=1) 104 | _url_frame.rowconfigure(0, weight=1) 105 | 106 | _url = StringVar() 107 | _url.set('http://localhost:8000') 108 | _url_entry = ttk.Entry( 109 | _url_frame, width=40, textvariable=_url) 110 | _url_entry.grid(row=0, column=0, sticky=(E, W, S, N), padx=5) 111 | 112 | _fetch_btn = ttk.Button( 113 | _url_frame, text='Fetch info', command=fetch_url) 114 | _fetch_btn.grid(row=0, column=1, sticky=W, padx=5) 115 | 116 | _img_frame = ttk.LabelFrame( 117 | _mainframe, text='Content', padding='9 0 0 0') 118 | _img_frame.grid(row=1, column=0, sticky=(N, S, E, W)) 119 | 120 | _images = StringVar() 121 | _img_listbox = Listbox( 122 | _img_frame, listvariable=_images, height=6, width=25) 123 | _img_listbox.grid(row=0, column=0, sticky=(E, W), pady=5) 124 | _scrollbar = ttk.Scrollbar( 125 | _img_frame, orient=VERTICAL, command=_img_listbox.yview) 126 | _scrollbar.grid(row=0, column=1, sticky=(S, N), pady=6) 127 | _img_listbox.configure(yscrollcommand=_scrollbar.set) 128 | 129 | _radio_frame = ttk.Frame(_img_frame) 130 | _radio_frame.grid(row=0, column=2, sticky=(N, S, W, E)) 131 | 132 | _choice_lbl = ttk.Label( 133 | _radio_frame, text="Choose how to save images") 134 | _choice_lbl.grid(row=0, column=0, padx=5, pady=5) 135 | 136 | _save_method = StringVar() 137 | _save_method.set('img') 138 | _img_only_radio = ttk.Radiobutton( 139 | _radio_frame, text='As Images', variable=_save_method, 140 | value='img') 141 | _img_only_radio.grid( 142 | row=1, column=0, padx=5, pady=2, sticky=W) 143 | _img_only_radio.configure(state='normal') 144 | _json_radio = ttk.Radiobutton( 145 | _radio_frame, text='As JSON', variable=_save_method, 146 | value='json') 147 | _json_radio.grid(row=2, column=0, padx=5, pady=2, sticky=W) 148 | 149 | _scrape_btn = ttk.Button( 150 | _mainframe, text='Scrape!', command=save) 151 | _scrape_btn.grid(row=2, column=0, sticky=E, pady=5) 152 | 153 | _status_frame = ttk.Frame( 154 | _root, relief='sunken', padding='2 2 2 2') 155 | _status_frame.grid(row=1, column=0, sticky=(E, W, S)) 156 | 157 | _status_msg = StringVar() 158 | _status_msg.set('Type a URL to start scraping...') 159 | _status = ttk.Label( 160 | _status_frame, textvariable=_status_msg, anchor=W) 161 | _status.grid(row=0, column=0, sticky=(E, W)) 162 | 163 | _root.mainloop() 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Learning Python 5 | 6 | Learning Python 7 | 8 | This is the code repository for [Learning Python](https://prod.packtpub.com/in/application-development/learning-python), published by Packt. 9 | 10 | **Learn to code like a professional with Python** 11 | 12 | ## What is this book about? 13 | Learning Python has a dynamic and varied nature. It reads easily and lays a good foundation for those who are interested in digging deeper. It has a practical and example-oriented approach through which both the introductory and the advanced topics are explained. Starting with the fundamentals of programming and Python, it ends by exploring very different topics, like GUIs, web apps and data science. The book takes you all the way to creating a fully fledged application. 14 | 15 | The book begins by exploring the essentials of programming, data structures and teaches you how to manipulate them. It then moves on to controlling the flow of a program and writing reusable and error proof code. You will then explore different programming paradigms that will allow you to find the best approach to any situation, and also learn how to perform performance optimization as well as effective debugging. Throughout, the book steers you through the various types of applications, and it concludes with a complete mini website built upon all the concepts that you learned. 16 | 17 | This book covers the following exciting features: 18 | 19 | * Get Python up and running on Windows, Mac, and Linux in no time 20 | * Grasp the fundamental concepts of coding, along with the basics of data structures and control flow. 21 | * Write elegant, reusable, and efficient code in any situation 22 | * Understand when to use the functional or the object oriented programming approach 23 | * Create bulletproof, reliable software by writing tests to support your code 24 | * Explore examples of GUIs, scripting, data science and web applications 25 | * Learn to be independent, capable of fetching any resource you need, as well as dig deeper 26 | 27 | If you feel this book is for you, get your [copy](https://www.amazon.in/Learning-Python-Fabrizio-Romano/dp/1783551712) today! 28 | 29 | https://www.packtpub.com/ 31 | 32 | ## Instructions and Navigations 33 | All of the code is organized into folders. For example, Chapter 2. 34 | 35 | **Following is what you need for this book:** 36 | Python is the most popular introductory teaching language in U.S. top computer science universities, so if you are new to software development, or maybe you have little experience, and would like to start off on the right foot, then this language and this book are what you need. Its amazing design and portability will help you become productive regardless of the environment you choose to work with. 37 | 38 | With the following software and hardware list you can run all code files present in the book (Chapter 1-16). 39 | ### Software and Hardware List 40 | | Chapter | Software required | OS required | 41 | | -------- | ------------------------------------ | ----------------------------------- | 42 | | All | Python 3.5 or higher | Windows, Mac OS X, and Linux (Any) | 43 | | All | Anaconda | Windows, Mac OS X, and Linux (Any) | 44 | 45 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://www.packtpub.com/sites/default/files/downloads/9781783551712_ColorImages.pdf). 46 | 47 | ### Related products 48 | * Python Essentials [[PACKT]](https://prod.packtpub.com/in/application-development/python-essentials) [[Amazon]](https://www.amazon.in/Python-Essentials-Steven-F-Lott/dp/1784390348) 49 | 50 | * Python Geospatial Analysis Essentials 51 | Essentials [[PACKT]](https://prod.packtpub.com/in/big-data-and-business-intelligence/python-geospatial-analysis-essentials) [[Amazon]](https://www.amazon.in/Python-Geospatial-Analysis-Essentials-Westra/dp/1782174516) 52 | 53 | * Python Requests Essentials 54 | Essentials [[PACKT]](https://prod.packtpub.com/in/networking-and-servers/python-requests-essentials) [[Amazon]](https://www.amazon.in/Python-Requests-Essentials-Rakesh-Chandra/dp/1784395412/) 55 | 56 | * Mastering pandas 57 | Essentials [[PACKT]](https://prod.packtpub.com/in/big-data-and-business-intelligence/mastering-pandas) [[Amazon]](https://www.amazon.in/Mastering-pandas-Femi-Anthony/dp/1783981962) 58 | 59 | 60 | ## Get to Know the Authors 61 | **Fabrizio Romano** 62 | was born in Italy in 1975. He holds a master's degree in computer science engineering from the University of Padova. He is also a certified Scrum master. Before Python, he has worked with several other languages, such as C/C++, Java, PHP, and C#. In 2011, he moved to London and started working as a Python developer for Glasses Direct, one of Europe's leading online prescription glasses retailers. He then worked as a senior Python developer for TBG (now Sprinklr), one of the world's leading companies in social media advertising. At TBG, he and his team collaborated with Facebook and Twitter. They were the first in the world to get access to the Twitter advertising API. He wrote the code that published the first geo-narrowcasted promoted tweet in the world using the API. He currently works as a senior platform developer at Student.com, a company that is revolutionizing the way international students find their perfect home all around the world. He has delivered talks on Teaching Python and TDD with Python at the last two editions of EuroPython and at Skillsmatter in London. 63 | 64 | 65 | 66 | ### Suggestions and Feedback 67 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 68 | 69 | 70 | -------------------------------------------------------------------------------- /ch12/pwdapi/tests/test_core/test_passwords.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from unittest import TestCase 3 | from unittest.mock import patch 4 | 5 | from nose_parameterized import parameterized, param 6 | from nose.tools import ( 7 | assert_equal, assert_dict_equal, assert_true) 8 | 9 | from core.passwords import PasswordValidator, PasswordGenerator 10 | 11 | 12 | class PasswordValidatorTestCase(TestCase): 13 | 14 | @parameterized.expand([ 15 | (False, ''), 16 | (False, ' '), 17 | (True, 'abcdefghijklmnopqrstuvwxyz'), 18 | (True, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 19 | (True, '0123456789'), 20 | (True, '!#$%&()*+-?@_|'), 21 | ]) 22 | def test_is_valid(self, valid, password): 23 | validator = PasswordValidator(password) 24 | assert_equal(valid, validator.is_valid()) 25 | 26 | @parameterized.expand( 27 | param.explicit(char) for char in '>]{<`\\;,[^/"\'~:}=.' 28 | ) 29 | def test_is_valid_invalid_chars(self, password): 30 | validator = PasswordValidator(password) 31 | assert_equal(False, validator.is_valid()) 32 | 33 | @parameterized.expand([ 34 | (0, ''), # 0-3: score 0 35 | (0, 'a'), # 0-3: score 0 36 | (0, 'aa'), # 0-3: score 0 37 | (0, 'aaa'), # 0-3: score 0 38 | (1, 'aaab'), # 4-7: score 1 39 | (1, 'aaabb'), # 4-7: score 1 40 | (1, 'aaabbb'), # 4-7: score 1 41 | (1, 'aaabbbb'), # 4-7: score 1 42 | (3, 'aaabbbbc'), # 8-11: score 3 43 | (3, 'aaabbbbcc'), # 8-11: score 3 44 | (3, 'aaabbbbccc'), # 8-11: score 3 45 | (3, 'aaabbbbcccc'), # 8-11: score 3 46 | (5, 'aaabbbbccccd'), # 12-15: score 5 47 | (5, 'aaabbbbccccdd'), # 12-15: score 5 48 | (5, 'aaabbbbccccddd'), # 12-15: score 5 49 | (5, 'aaabbbbccccdddd'), # 12-15: score 5 50 | ]) 51 | def test__score_length(self, score, password): 52 | validator = PasswordValidator(password) 53 | assert_equal(score, validator._score_length()) 54 | 55 | def test__score_length_sixteen_plus(self): 56 | # all password whose length is 16+ score 7 points 57 | password = 'x' * 255 58 | for length in range(16, len(password)): 59 | validator = PasswordValidator(password[:length]) 60 | assert_equal(7, validator._score_length()) 61 | 62 | @parameterized.expand([ 63 | (0, '0123456789!#$%&()*+-?@_|'), 64 | (1, 'abc'), 65 | (1, 'ABC'), 66 | (3, 'aA'), 67 | ]) 68 | def test__score_case(self, score, password): 69 | validator = PasswordValidator(password) 70 | assert_equal(score, validator._score_case()) 71 | 72 | @parameterized.expand( 73 | param.explicit(char) for char in '0123456789' 74 | ) 75 | def test__score_numbers(self, password): 76 | validator = PasswordValidator(password) 77 | assert_equal(2, validator._score_numbers()) 78 | 79 | @parameterized.expand([ 80 | ('abcdefghijklmnopqrstuvwxyz'), 81 | ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 82 | ('!#$%&()*+-?@_|'), 83 | ]) 84 | def test__score_numbers_no_numbers(self, password): 85 | validator = PasswordValidator(password) 86 | assert_equal(0, validator._score_numbers()) 87 | 88 | @parameterized.expand( 89 | param.explicit(char) for char in '!#$%&()*+-?@_|' 90 | ) 91 | def test__score_special(self, password): 92 | validator = PasswordValidator(password) 93 | assert_equal(4, validator._score_special()) 94 | 95 | @parameterized.expand([ 96 | ('abcdefghijklmnopqrstuvwxyz'), 97 | ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 98 | ('0123456789'), 99 | ]) 100 | def test__score_special_no_special(self, password): 101 | validator = PasswordValidator(password) 102 | assert_equal(0, validator._score_special()) 103 | 104 | @parameterized.expand([ 105 | (0, 'abcdef'), # 6:0 = 0 (6 chars, 0 digits) 106 | (1, 'a0'), # 1:1 = 1 107 | (3, 'abc1'), # 3:1 = 3 108 | (1, 'a123'), # 1:3 = 1 109 | (7, 'abcdefg1'), # 7:1 = 7 110 | (7, 'aaaaaaa1'), # 7:1 = 7 (repeated characters) 111 | (7, 'abcdefgh1'), # 8:1 = 7 (limitation) 112 | ]) 113 | def test__score_ratio(self, score, password): 114 | validator = PasswordValidator(password) 115 | assert_equal(score, validator._score_ratio()) 116 | 117 | @patch.object(PasswordValidator, '_score_length') 118 | @patch.object(PasswordValidator, '_score_case') 119 | @patch.object(PasswordValidator, '_score_numbers') 120 | @patch.object(PasswordValidator, '_score_special') 121 | @patch.object(PasswordValidator, '_score_ratio') 122 | def test_score( 123 | self, 124 | _score_ratio_mock, 125 | _score_special_mock, 126 | _score_numbers_mock, 127 | _score_case_mock, 128 | _score_length_mock): 129 | 130 | _score_ratio_mock.return_value = 2 131 | _score_special_mock.return_value = 3 132 | _score_numbers_mock.return_value = 5 133 | _score_case_mock.return_value = 7 134 | _score_length_mock.return_value = 11 135 | 136 | expected_result = { 137 | 'length': 11, 138 | 'case': 7, 139 | 'numbers': 5, 140 | 'special': 3, 141 | 'ratio': 2, 142 | 'total': 28, 143 | } 144 | 145 | validator = PasswordValidator('') 146 | 147 | assert_dict_equal(expected_result, validator.score()) 148 | 149 | 150 | class PasswordGeneratorTestCase(TestCase): 151 | 152 | def test__generate_password_length(self): 153 | for length in range(300): 154 | assert_equal( 155 | length, 156 | len(PasswordGenerator._generate_password(length)) 157 | ) 158 | 159 | def test__generate_password_validity(self): 160 | for length in range(1, 300): 161 | password = PasswordGenerator._generate_password( 162 | length) 163 | assert_true(PasswordValidator(password).is_valid()) 164 | 165 | def test__generate_candidate(self): 166 | score, password = ( 167 | PasswordGenerator._generate_candidate(42)) 168 | expected_score = PasswordValidator(password).score() 169 | assert_equal(expected_score['total'], score) 170 | 171 | @patch.object(PasswordGenerator, '_generate_candidate') 172 | def test__generate(self, _generate_candidate_mock): 173 | # checks `generate` returns the highest score candidate 174 | _generate_candidate_mock.side_effect = [ 175 | (16, '&a69Ly+0H4jZ'), 176 | (17, 'UXaF4stRfdlh'), 177 | (21, 'aB4Ge_KdTgwR'), # the winner 178 | (12, 'IRLT*XEfcglm'), 179 | (16, '$P92-WZ5+DnG'), 180 | (18, 'Xi#36jcKA_qQ'), 181 | (19, '?p9avQzRMIK0'), 182 | (17, '4@sY&bQ9*H!+'), 183 | (12, 'Cx-QAYXG_Ejq'), 184 | (18, 'C)RAV(HP7j9n'), 185 | ] 186 | assert_equal( 187 | (21, 'aB4Ge_KdTgwR'), PasswordGenerator.generate(12)) 188 | --------------------------------------------------------------------------------