diff --git a/Exception_Handling_and_Debugging.md b/Exception_Handling_and_Debugging.md index 0ae2379..be784d1 100644 --- a/Exception_Handling_and_Debugging.md +++ b/Exception_Handling_and_Debugging.md @@ -37,7 +37,7 @@ while True: print("Square of entered number is: {}".format(usr_num * usr_num)) ``` -* `except` can be used for particular error (in this case `ValueError`) or without argument to handle any kind of error +* `except` can be used for particular error (in this case `ValueError`) ``` $ ./user_input_exception.py @@ -51,9 +51,10 @@ Square of entered number is: 9 **Further Reading** -* [Python docs - errors and exception handling](https://docs.python.org/3/tutorial/errors.html) -* [Python docs - raising exceptions](https://docs.python.org/3/tutorial/errors.html#raising-exceptions) +* [Python docs - errors, exception handling and raising exceptions](https://docs.python.org/3/tutorial/errors.html) * [Python docs - built-in exceptions](https://docs.python.org/3/library/exceptions.html#bltin-exceptions) +* [stackoverflow - exception message capturing](https://stackoverflow.com/questions/4690600/python-exception-message-capturing) +* [stackoverflow - avoid bare exceptions](https://stackoverflow.com/questions/14797375/should-i-always-specify-an-exception-type-in-except-statements) * [Python docs - pass statement](https://docs.python.org/3/reference/simple_stmts.html#grammar-token-pass_stmt)
diff --git a/Exercises.md b/Exercises.md index 0f06e71..727358f 100644 --- a/Exercises.md +++ b/Exercises.md @@ -1,18 +1,24 @@ # Exercises -* [Variables and Print](#variables-and-print) -* [Functions](#functions) -* [Control structures](#control-structures) -* [List](#list) -* [File](#file) -* [Text processing](#text-processing) -* [Misc](#misc) +1) [Variables and Print](#variables-and-print) +2) [Functions](#functions) +3) [Control structures](#control-structures) +4) [List](#list) +5) [File](#file) +6) [Text processing](#text-processing) +7) [Misc](#misc)
-## Variables and Print +For some questions, Python program with assert statements is provided to automatically test your solution in the [exercise_files](https://github.com/learnbyexample/Python_Basics/tree/master/exercise_files) directory - for ex: [Q2a - length of integer numbers](https://github.com/learnbyexample/Python_Basics/blob/master/exercise_files/q2a_int_length.py). The directory also has sample input text files. -Ask user information, for ex: `name`, `department`, `college` etc and display them using print function +You can also solve these exercises on [repl.it](https://repl.it/community/classrooms/52626), with an option to submit them for review. + +
+ +## 1) Variables and Print + +**Q1a)** Ask user information, for ex: `name`, `department`, `college` etc and display them using print function ``` # Sample of how program might ask user input and display output afterwards @@ -30,9 +36,9 @@ College : PSG Tech
-## Functions +## 2) Functions -* Returns length of integer numbers +**Q2a)** Returns length of integer numbers ```python >>> len_int(962306349871524124750813401378124) @@ -45,14 +51,11 @@ College : PSG Tech # bonus: handle -ve numbers and check for input type >>> len_int(-42) 2 ->>> len_int('a') -Traceback (most recent call last): - File "", line 1, in - File "", line 3, in len_int +# len_int('a') should give TypeError: provide only integer input ``` -* Returns True/False - two strings are same irrespective of lowercase/uppercase +**Q2b)** Returns True/False - two strings are same irrespective of lowercase/uppercase ```python >>> str_cmp('nice', 'nice') @@ -65,27 +68,68 @@ True False ``` -* Returns True/False - two strings are anagrams (assume input consists of alphabets only) +**Q2c)** Returns True/False - two strings are anagrams (assume input consists of alphabets only) ```python >>> str_anagram('god', 'Dog') True >>> str_anagram('beat', 'table') False +>>> str_anagram('Tap', 'paT') +True >>> str_anagram('beat', 'abet') True ``` +**Q2d)** Returns corresponding integer or floating-point number (See [Number and String data types](./Number_and_String_datatypes.md) chapter for details) + +```python +# number input +>>> num(3) +3 +>>> num(0x1f) +31 +>>> num(3.32) +3.32 + +# string input +>>> num('123') +123 +>>> num('-78') +-78 +>>> num(" 42 \n ") +42 +>>> num('3.14') +3.14 +>>> num('3.982e5') +398200.0 + +>>> s = '56' +>>> num(s) + 44 +100 +``` + +Other than integer or floating, only string data type should be accepted. Also, provide custom error message if input cannot be converted + +```python +# num(['1', '2.3']) +TypeError: not a valid input + +# num('foo') +ValueError: could not convert string to int or float +``` +
-## Control structures +## 3) Control structures + +**Q3a)** Write a function that returns -* Write a function that returns - * 'Good' for numbers divisable by 7 - * 'Food' for numbers divisable by 6 - * 'Universe' for numbers divisable by 42 - * 'Oops' for all other numbers - * Only one output, divisable by 42 takes precedence +* 'Good' for numbers divisible by 7 +* 'Food' for numbers divisible by 6 +* 'Universe' for numbers divisible by 42 +* 'Oops' for all other numbers +* Only one output, divisible by 42 takes precedence ```python >>> six_by_seven(66) @@ -121,7 +165,7 @@ True 100 Oops ``` -* Print all numbers from 1 to 1000 which reads the same in reversed form in both binary and decimal format +**Q3b)** Print all numbers from 1 to 1000 which reads the same in reversed form in both binary and decimal format ``` $ ./dec_bin.py @@ -158,9 +202,9 @@ $ ./dec_bin_oct_hex.py
-## List +## 4) List -* Write a function that returns product of all numbers of a list +**Q4a)** Write a function that returns product of all numbers of a list ```python >>> product([1, 4, 21]) @@ -172,8 +216,6 @@ $ ./dec_bin_oct_hex.py *bonus*: works on any kind of iterable ```python ->>> product(b) -84 >>> product((-3, 11, 2)) -66 >>> product({8, 300}) @@ -185,8 +227,9 @@ $ ./dec_bin_oct_hex.py # can you identify what mathematical function the last one performs? ``` -* Write a function that returns nth lowest number of a list (or iterable in general) - * by default, return the lowest if second argument not specified +**Q4b)** Write a function that returns nth lowest number of a list (or iterable in general). Return the lowest if second argument not specified + +*Note* that if a list contains duplicates, they should be handled before determining nth lowest ```python >>> nums = [42, 23421341, 234.2e3, 21, 232, 12312, -2343] @@ -205,16 +248,38 @@ IndexError: list index out of range -2 >>> nth_lowest(nums, 4) 3 +>>> nth_lowest(nums, 5) +4 >>> nth_lowest('unrecognizable', 3) 'c' +>>> nth_lowest('abracadabra', 4) +'d' +``` + +**Q4c)** Write a function that accepts a string input and returns slices + +* if input string is less than 3 characters long, return a list with input string as the only element +* otherwise, return list with all string slices greater than 1 character long +* order of slices should be same as shown in examples below + +```python +>>> word_slices('i') +['i'] +>>> word_slices('to') +['to'] + +>>> word_slices('are') +['ar', 'are', 're'] +>>> word_slices('table') +['ta', 'tab', 'tabl', 'table', 'ab', 'abl', 'able', 'bl', 'ble', 'le'] ```
-## File +## 5) File -* Print sum of all numbers from a file containing only single column and all numbers +**Q5a)** Print sum of all numbers from a file containing only single column and all numbers ``` $ cat f1.txt @@ -230,7 +295,7 @@ $ ./col_sum.py 10485.14 ``` -* Print sum of all numbers (assume only positive integer numbers) from a file containing arbitrary string +**Q5b)** Print sum of all numbers (assume only positive integer numbers) from a file containing arbitrary string ``` $ cat f2.txt @@ -243,12 +308,45 @@ $ ./extract_sum.py 2298 ``` +**Q5c)** Sort file contents in alphabetic order based on each line's extension + +* extension here is defined as the string after the last `.` in the line +* if line doesn't have a `.`, those lines should come before lines with `.` +* sorting should be case-insensitive +* use rest of string as tie-breaker if there are more than one line with same extension +* assume input file is ASCII encoded and small enough to fit in memory + +*bonus*: instead of printing results to stdout, change the input file itself with sorted result + +```bash +$ cat f3.txt +power.Log +foo.123.txt +list +report_12.log +baz.TXT +hello.RB +loop.do.rb +Fav_books + +$ ./sort_by_ext.py +Fav_books +list +power.Log +report_12.log +hello.RB +loop.do.rb +baz.TXT +foo.123.txt +``` +
-## Text processing +## 6) Text processing + +**Q6a)** Check if two words are same or differ by only one character (irrespective of case), input strings should have same length -* Check if two words are same or differ by only one character (irrespective of case) - * input strings should have same length +See also [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) ```python >>> is_one_char_diff('bar', 'bar') @@ -272,8 +370,9 @@ False False ``` -* Check if a word is in ascending/descending alphabetic order or not (irrespective of case) - * can you think of a way to do it only using built-in functions and string methods? +**Q6b)** Check if a word is in ascending/descending alphabetic order or not (irrespective of case) + +Can you think of a way to do it only using built-in functions and string methods? ```python >>> is_alpha_order('bot') @@ -304,11 +403,62 @@ True False ``` +**Q6c)** Find the maximum nested depth of curly braces + +Unbalanced or wrongly ordered braces should return `-1` + +Iterating over input string is one way to solve this, another is to use regular expressions + +```python +>>> max_nested_braces('a*b') +0 +>>> max_nested_braces('{a+2}*{b+c}') +1 +>>> max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') +4 +>>> max_nested_braces('a*b+{}') +1 +>>> max_nested_braces('}a+b{') +-1 +>>> max_nested_braces('a*b{') +-1 +``` + +*bonus*: empty braces, i.e `{}` should return `-1` + +```python +>>> max_nested_braces('a*b+{}') +-1 +>>> max_nested_braces('a*{b+{}+c*{e*3.14}}') +-1 +``` +
-## Misc +## 7) Misc + +**Q7a)** Play a song (**hint** use `subprocess` module) + +**Q7b)** Open a browser along with any link, for ex: https://github.com/learnbyexample/Python_Basics (**hint** use `webbrowser` module) + +**Q7c)** Write a function that + +* accepts a filesystem path(default) or a url(indicated by True as second argument) +* returns the longest word(here word is defined as one or more consecutive sequence of alphabets of either case) +* assume that input encoding is **utf-8** and small enough to fit in memory and that there's only one distinct longest word + +```python +>>> ip_path = 'poem.txt' +>>> longest_word(ip_path) +'Violets' + +>>> ip_path = 'https://www.gutenberg.org/files/60/60.txt' +>>> longest_word(ip_path, True) +'misunderstandings' +``` + +
+ +
-* Play a song - * **hint** use `subprocess` module -* Open a browser along with any link, for ex: https://github.com/learnbyexample/Python_Basics - * **hint** use `webbrowser` module +For reference solutions, see [exercise_solutions](https://github.com/learnbyexample/Python_Basics/tree/master/exercise_solutions) directory diff --git a/Functions.md b/Functions.md index d6a834d..22486dd 100644 --- a/Functions.md +++ b/Functions.md @@ -233,6 +233,8 @@ Error!! Not a valid input >>> op_fmt = '{} + {} = {}' >>> op_fmt.format(num1, num2, num1 + num2) '42 + 7 = 49' +>>> op_fmt.format(num1, 29, num1 + 29) +'42 + 29 = 71' # and of course the expression can be used inside print directly >>> print('{} + {} = {}'.format(num1, num2, num1 + num2)) @@ -306,7 +308,32 @@ Error!! Not a valid input 42 ``` +* similar to the `r` raw string prefix, using `f` prefix allows to represent format strings + * introduced in Python v3.6 +* similar to `str.format()`, the variables/expressions are specified within `{}` + +```python +>>> num1 = 42 +>>> num2 = 7 +>>> f'{num1} + {num2} = {num1 + num2}' +'42 + 7 = 49' +>>> print(f'{num1} + {num2} = {num1 + num2}') +42 + 7 = 49 + +>>> appx_pi = 22 / 7 +>>> f'{appx_pi:08.3f}' +'0003.143' + +>>> f'{20:x}' +'14' +>>> f'{20:#x}' +'0x14' +``` + +**Further Reading** + * [Python docs - formatstrings](https://docs.python.org/3/library/string.html#formatstrings) - for more info and examples +* [Python docs - f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings) - for more examples and caveats
diff --git a/Lists.md b/Lists.md index c436fe3..143acb6 100644 --- a/Lists.md +++ b/Lists.md @@ -301,10 +301,14 @@ IndexError: list index out of range * using [del](https://docs.python.org/3/reference/simple_stmts.html#del) to delete elements ```python ->>> books = ['Harry Potter', 'Sherlock Holmes', 'To Kill a Mocking Bird'] ->>> del books[1] ->>> books -['Harry Potter', 'To Kill a Mocking Bird'] +>>> nums = [1.2, -0.2, 0, 2, 4, 23] +>>> del nums[1] +>>> nums +[1.2, 0, 2, 4, 23] +# can use slicing notation as well +>>> del nums[1:4] +>>> nums +[1.2, 23] >>> list_2D = [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] >>> del list_2D[0][1] diff --git a/Number_and_String_datatypes.md b/Number_and_String_datatypes.md index 136ac7a..c6596d5 100644 --- a/Number_and_String_datatypes.md +++ b/Number_and_String_datatypes.md @@ -87,6 +87,23 @@ Variable data type is automatically determined by Python. They only need to be a 25 ``` +* `_` can be used between digits for readability + * introduced in Python v3.6 + +```python +>>> 1_000_000 +1000000 +>>> 1_00.3_352 +100.3352 +>>> 0xff_ab1 +1047217 + +# f-strings formatting explained in a later chapter +>>> num = 34 ** 32 +>>> print(f'{num:_}') +10_170_102_859_315_411_774_579_628_461_341_138_023_025_901_305_856 +``` + **Further Reading** * [Python docs - numbers](https://docs.python.org/3/tutorial/introduction.html#numbers) @@ -162,6 +179,11 @@ Hello World >>> word = 'buffalo ' >>> print(word * 8) buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo + +# Python v3.6 allows variable interpolation with f-strings +>>> msg = f'{str1} there' +>>> msg +'Hello there' ``` * Triple quoted strings @@ -201,6 +223,7 @@ $ **Further Reading** * [Python docs - strings](https://docs.python.org/3/tutorial/introduction.html#strings) +* [Python docs - f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings) - for more examples and caveats * [Python docs - List of Escape Sequences and more info on strings](https://docs.python.org/3/reference/lexical_analysis.html#strings) * [Python docs - Binary Sequence Types](https://docs.python.org/3/library/stdtypes.html#binary-sequence-types-bytes-bytearray-memoryview) * [formatting triple quoted strings](https://stackoverflow.com/questions/3877623/in-python-can-you-have-variables-within-triple-quotes-if-so-how) @@ -268,5 +291,3 @@ True * [Python docs - Numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex) - complete list of operations and precedence * [Python docs - String methods](https://docs.python.org/3/library/stdtypes.html#string-methods) - - diff --git a/README.md b/README.md index 95c70df..a52652f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,25 @@ -Use [![Join the chat at https://gitter.im/learnbyexample/scripting_course](https://badges.gitter.im/learnbyexample/scripting_course.svg)](https://gitter.im/learnbyexample/scripting_course) if you need help, have suggestions, etc -
+--- + +:warning: :warning: I'm archiving this repo, as I don't intend to work on this repo further. + +I'm re-using materials in this repo for the **100 Page Python Intro** book (https://github.com/learnbyexample/100_page_python_intro). + +I'm also working on **Practice Python Projects** book (https://github.com/learnbyexample/practice_python_projects), which I had intended in this repo for the `mini_projects` folder. + +--- + +


+ # Python Basics Introduction to Python - Syntax, working with Shell commands, Files, Text Processing, and more... * Suitable for a one/two day workshop for Python beginners -* [Python curated resources](https://github.com/learnbyexample/scripting_course/blob/master/Python_curated_resources.md) for more complete resources list, including tutorials for beginners -* For more related resources, visit [scripting course](https://github.com/learnbyexample/scripting_course) +* Visit [Python re(gex)?](https://github.com/learnbyexample/py_regular_expressions) repo for a book on regular expressions +* [Python resources for everybody](https://learnbyexample.github.io/py_resources/) for a curated and searchable collection, including resources for complete beginners to programming +* For more related resources, visit [scripting course](https://github.com/learnbyexample/scripting_course) and my programming blog https://learnbyexample.github.io
@@ -48,10 +59,19 @@ Introduction to Python - Syntax, working with Shell commands, Files, Text Proces
+## Contributing + +* Please open an issue for typos/bugs/suggestions/etc + * As this repo is no longer actively worked upon, **please do not submit pull requests** +* Share the repo with friends/colleagues, on social media, etc to help reach other learners +* In case you need to reach me, mail me at `echo 'bGVhcm5ieWV4YW1wbGUubmV0QGdtYWlsLmNvbQo=' | base64 --decode` or send a DM via [twitter](https://twitter.com/learn_byexample) + +
+ # ebook * Read as ebook on [gitbook](https://learnbyexample.gitbooks.io/python-basics/content/index.html) -* Download ebook for offline reading - [link](https://www.gitbook.com/book/learnbyexample/python-basics/details) +* All `legacy.gitbook.com` links are now automatically redirected to `gitbook.com`, so there's no longer an option to download ebooks for offline reading
@@ -59,6 +79,7 @@ Introduction to Python - Syntax, working with Shell commands, Files, Text Proces * [automatetheboringstuff](https://automatetheboringstuff.com/) for getting me started with Python * [/r/learnpython/](https://www.reddit.com/r/learnpython/) - helpful forum for beginners and experienced programmers alike +* [stackoverflow](https://stackoverflow.com/) - for getting answers to pertinent questions as well as sharpening skills by understanding and answering questions * [Devs and Hackers](http://devup.in/) - helpful slack group * [Weekly Coders, Hackers & All Tech related thread](https://www.reddit.com/r/india/search?q=Weekly+Coders%2C+Hackers+%26+All+Tech+related+thread+author%3Aavinassh&restrict_sr=on&sort=new&t=all) - for suggestions and critique @@ -67,3 +88,4 @@ Introduction to Python - Syntax, working with Shell commands, Files, Text Proces # License This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/) + diff --git a/Sequence_Set_Dict_data_types.md b/Sequence_Set_Dict_data_types.md index 4efa0db..8b5cba8 100644 --- a/Sequence_Set_Dict_data_types.md +++ b/Sequence_Set_Dict_data_types.md @@ -5,8 +5,8 @@ * [Set](#set) * [Dictionary](#dictionary) -We have already seen Sequence types in previous chapters - strings, ranges and lists -We'll see some more operations on strings followed by Tuple, Set and Dict types in this chapter +We have already seen Sequence types in previous chapters - strings, ranges and lists. Tuple is another sequence type +We'll see some more operations on strings followed by Tuple, Set and Dict in this chapter
@@ -100,7 +100,8 @@ True ### Tuples -* Tuples can be thought of as sort of lists but immutable +* Tuples are similar to lists but immutable and useful in other ways too +* Individual elements can be both mutable/immutable ```python >>> north_dishes = ('Aloo tikki', 'Baati', 'Khichdi', 'Makki roti', 'Poha') @@ -163,6 +164,15 @@ Poha >>> b 5 +>>> c = 'foo' +>>> a, b, c = c, a, b +>>> a +'foo' +>>> b +20 +>>> c +5 + >>> def min_max(arr): ... return min(arr), max(arr) ... @@ -212,6 +222,7 @@ Poha ``` * [Python docs - tuple](https://docs.python.org/3/library/stdtypes.html#tuple) +* [Python docs - tuple tutorial](https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences)
@@ -294,6 +305,7 @@ True ### Dictionary * `dict` types can be thought of as unordered list of `key:value` pairs or a named list of items +* up to Python v3.5 (and some implementations of v3.6) do not retain order of insertion of dict elements ```python >>> marks = {'Rahul' : 86, 'Ravi' : 92, 'Rohit' : 75} @@ -380,7 +392,35 @@ False Try the 'East' speciality 'rosgulla' today ``` +* From Python v3.7 onwards, dict implementation will retain insertion order + * some implementations like the reference CPython implementation for v3.6 also retains the insertion order + +```python +>>> marks = {'Rahul' : 86, 'Ravi' : 92, 'Rohit' : 75, 'Rajan': 79} +>>> marks +{'Rahul': 86, 'Ravi': 92, 'Rohit': 75, 'Rajan': 79} + +>>> for name, mark in marks.items(): +... print(f'{name:5s}: {mark}') +... +Rahul: 86 +Ravi : 92 +Rohit: 75 +Rajan: 79 + +>>> del marks['Ravi'] +>>> marks +{'Rahul': 86, 'Rohit': 75, 'Rajan': 79} + +>>> marks['Ranjit'] = 65 +>>> marks +{'Rahul': 86, 'Rohit': 75, 'Rajan': 79, 'Ranjit': 65} +``` + **Further Reading** * [Python docs - dict](https://docs.python.org/3/library/stdtypes.html#dict) * [Python docs - pprint](https://docs.python.org/3/library/pprint.html) +* [detailed tutorial on dict](http://www.sharats.me/posts/the-python-dictionary/) +* [Using dict to eliminate duplicates while retaining order](https://twitter.com/raymondh/status/944125570534621185) + diff --git a/Text_Processing.md b/Text_Processing.md index f89031a..9033677 100644 --- a/Text_Processing.md +++ b/Text_Processing.md @@ -226,144 +226,206 @@ False ### Regular Expressions -* Handy reference of regular expression elements +* Handy reference of regular expression (RE) elements | Meta characters | Description | | ------------- | ----------- | -| ^ | anchor, match from beginning of string | -| $ | anchor, match end of string | -| . | Match any character except newline character \n | +| `\A` | anchor to restrict matching to beginning of string | +| `\Z` | anchor to restrict matching to end of string | +| `^` | anchor to restrict matching to beginning of line | +| `$` | anchor to restrict matching to end of line | +| `.` | Match any character except newline character `\n` | | | | OR operator for matching multiple patterns | -| () | for grouping patterns and also extraction | -| [] | Character class - match one character among many | -| \^ | use \ to match meta characters like ^ | +| `(RE)` | capturing group | +| `(?:RE)` | non-capturing group | +| `[]` | Character class - match one character among many | +| `\^` | prefix `\` to literally match meta characters like `^` |
-| Quantifiers | Description | +| Greedy Quantifiers | Description | | ------------- | ----------- | -| * | Match zero or more times the preceding character | -| + | Match one or more times the preceding character | -| ? | Match zero or one times the preceding character | -| {n} | Match exactly n times | -| {n,} | Match at least n times | -| {n,m} | Match at least n times but not more than m times | +| `*` | Match zero or more times | +| `+` | Match one or more times | +| `?` | Match zero or one times | +| `{m,n}` | Match `m` to `n` times (inclusive) | +| `{m,}` | Match at least m times | +| `{,n}` | Match up to `n` times (including `0` times) | +| `{n}` | Match exactly n times | + +Appending a `?` to greedy quantifiers makes them non-greedy
| Character classes | Description | | ------------- | ----------- | -| [aeiou] | Match any vowel | -| \[^aeiou] | ^ inverts selection, so this matches any consonant | -| [a-f] | Match any of abcdef character | -| \d | Match a digit, same as [0-9] | -| \D | Match non-digit, same as \[^0-9] or \[^\d] | -| \w | Match alphanumeric and underscore character, same as [a-zA-Z_] | -| \W | Match non-alphanumeric and underscore character, same as \[^a-zA-Z_] or \[^\w] | -| \s | Match white-space character, same as [\ \t\n\r\f\v] | -| \S | Match non white-space character, same as \[^\s] | -| \b | word boundary, word defined as sequence of alphanumeric characters | -| \B | not a word boundary | +| `[aeiou]` | Match any vowel | +| `[^aeiou]` | `^` inverts selection, so this matches any consonant | +| `[a-f]` | `-` defines a range, so this matches any of abcdef characters | +| `\d` | Match a digit, same as `[0-9]` | +| `\D` | Match non-digit, same as `[^0-9]` or `[^\d]` | +| `\w` | Match alphanumeric and underscore character, same as `[a-zA-Z0-9_]` | +| `\W` | Match non-alphanumeric and underscore character, same as `[^a-zA-Z0-9_]` or `[^\w]` | +| `\s` | Match white-space character, same as `[\ \t\n\r\f\v]` | +| `\S` | Match non white-space character, same as `[^\s]` | +| `\b` | word boundary, see `\w` for characters constituting a word | +| `\B` | not a word boundary |
-| Compilation Flags | Description | +| Flags | Description | | ------------- | ----------- | -| re.I | ignore case | -| re.M | multiline mode, ^ and $ anchors work on internal lines | -| re.S | singleline mode, . will also match \n | -| re.V | verbose mode, for better readability and adding comments | +| `re.I` | Ignore case | +| `re.M` | Multiline mode, `^` and `$` anchors work on lines | +| `re.S` | Singleline mode, `.` will also match `\n` | +| `re.X` | Verbose mode, for better readability and adding comments | -* [Python docs - Compilation Flags](https://docs.python.org/3/howto/regex.html#compilation-flags) - for more details and long names for flags +See [Python docs - Compilation Flags](https://docs.python.org/3/howto/regex.html#compilation-flags) for more details and long names for flags
| Variable | Description | | ------------- | ----------- | -| \1, \2, \3 etc | backreferencing matched patterns | -| \g<1>, \g<2>, \g<3> etc | backreferencing matched patterns, useful to differentiate numbers and backreferencing | +| `\1`, `\2`, `\3` ... `\99` | backreferencing matched patterns | +| `\g<1>`, `\g<2>`, `\g<3>` ... | backreferencing matched patterns, prevents ambiguity | +| `\g<0>` | entire matched portion | + +`\0` and `\100` onwards are considered as octal values, hence cannot be used as backreference.
### Pattern matching and extraction -* matching/extracting sequence of characters -* use `re.search()` to see if a string contains a pattern or not -* use `re.findall()` to get a list of matching patterns -* use `re.split()` to get a list from splitting a string based on a pattern -* their syntax given below +To match/extract sequence of characters, use + +* `re.search()` to see if input string contains a pattern or not +* `re.findall()` to get a list of all matching portions +* `re.finditer()` to get an iterator of `re.Match` objects of all matching portions +* `re.split()` to get a list from splitting input string based on a pattern + +Their syntax is as follows: ```python re.search(pattern, string, flags=0) re.findall(pattern, string, flags=0) +re.finditer(pattern, string, flags=0) re.split(pattern, string, maxsplit=0, flags=0) ``` +* As a good practice, always use **raw strings** to construct RE, unless other formats are required + * this will avoid clash of backslash escaping between RE and normal quoted strings +* examples for `re.search` + ```python ->>> import re ->>> string = "This is a sample string" +>>> sentence = 'This is a sample string' ->>> bool(re.search('is', string)) +# using normal string methods +>>> 'is' in sentence True - ->>> bool(re.search('this', string)) +>>> 'xyz' in sentence False ->>> bool(re.search('this', string, re.I)) +# need to load the re module before use +>>> import re +# check if 'sentence' contains the pattern described by RE argument +>>> bool(re.search(r'is', sentence)) True - ->>> bool(re.search('T', string)) +>>> bool(re.search(r'this', sentence, flags=re.I)) True +>>> bool(re.search(r'xyz', sentence)) +False +``` ->>> bool(re.search('is a', string)) -True +* examples for `re.findall` ->>> re.findall('i', string) -['i', 'i', 'i'] +```python +# match whole word par with optional s at start and e at end +>>> re.findall(r'\bs?pare?\b', 'par spar apparent spare part pare') +['par', 'spar', 'spare', 'pare'] + +# numbers >= 100 with optional leading zeros +>>> re.findall(r'\b0*[1-9]\d{2,}\b', '0501 035 154 12 26 98234') +['0501', '154', '98234'] + +# if multiple capturing groups are used, each element of output +# will be a tuple of strings of all the capture groups +>>> re.findall(r'(x*):(y*)', 'xx:yyy x: x:yy :y') +[('xx', 'yyy'), ('x', ''), ('x', 'yy'), ('', 'y')] + +# normal capture group will hinder ability to get whole match +# non-capturing group to the rescue +>>> re.findall(r'\b\w*(?:st|in)\b', 'cost akin more east run against') +['cost', 'akin', 'east', 'against'] + +# useful for debugging purposes as well before applying substitution +>>> re.findall(r't.*?a', 'that is quite a fabricated tale') +['tha', 't is quite a', 'ted ta'] ``` -* using regular expressions -* use the `r''` format when using regular expression elements +* examples for `re.split` ```python ->>> string -'This is a sample string' - ->>> re.findall('is', string) -['is', 'is'] +# split based on one or more digit characters +>>> re.split(r'\d+', 'Sample123string42with777numbers') +['Sample', 'string', 'with', 'numbers'] ->>> re.findall('\bis', string) -[] +# split based on digit or whitespace characters +>>> re.split(r'[\d\s]+', '**1\f2\n3star\t7 77\r**') +['**', 'star', '**'] ->>> re.findall(r'\bis', string) -['is'] +# to include the matching delimiter strings as well in the output +>>> re.split(r'(\d+)', 'Sample123string42with777numbers') +['Sample', '123', 'string', '42', 'with', '777', 'numbers'] ->>> re.findall(r'\w+', string) -['This', 'is', 'a', 'sample', 'string'] +# use non-capturing group if capturing is not needed +>>> re.split(r'hand(?:y|ful)', '123handed42handy777handful500') +['123handed42', '777', '500'] +``` ->>> re.split(r'\s+', string) -['This', 'is', 'a', 'sample', 'string'] +* backreferencing ->>> re.split(r'\d+', 'Sample123string54with908numbers') -['Sample', 'string', 'with', 'numbers'] +```python +# whole words that have at least one consecutive repeated character +>>> words = ['effort', 'flee', 'facade', 'oddball', 'rat', 'tool'] ->>> re.split(r'(\d+)', 'Sample123string54with908numbers') -['Sample', '123', 'string', '54', 'with', '908', 'numbers'] +>>> [w for w in words if re.search(r'\b\w*(\w)\1\w*\b', w)] +['effort', 'flee', 'oddball', 'tool'] ``` -* backreferencing +* The `re.search` function returns a `re.Match` object from which various details can be extracted +like the matched portion of string, location of matched portion, etc +* **Note** that output here is shown for Python version **3.7** ```python ->>> quote = "So many books, so little time" +>>> re.search(r'b.*d', 'abc ac adc abbbc') + +# retrieving entire matched portion +>>> re.search(r'b.*d', 'abc ac adc abbbc')[0] +'bc ac ad' + +# capture group example +>>> m = re.search(r'a(.*)d(.*a)', 'abc ac adc abbbc') +# to get matched portion of second capture group +>>> m[2] +'c a' +# to get a tuple of all the capture groups +>>> m.groups() +('bc ac a', 'c a') +``` ->>> re.search(r'([a-z]{2,}).*\1', quote, re.I) -<_sre.SRE_Match object; span=(0, 17), match='So many books, so'> +* examples for `re.finditer` ->>> re.search(r'([a-z])\1', quote, re.I) -<_sre.SRE_Match object; span=(9, 11), match='oo'> +```python +>>> m_iter = re.finditer(r'(x*):(y*)', 'xx:yyy x: x:yy :y') +>>> [(m[1], m[2]) for m in m_iter] +[('xx', 'yyy'), ('x', ''), ('x', 'yy'), ('', 'y')] ->>> re.findall(r'([a-z])\1', quote, re.I) -['o', 't'] +>>> m_iter = re.finditer(r'ab+c', 'abc ac adc abbbc') +>>> for m in m_iter: +... print(m.span()) +... +(0, 3) +(11, 16) ```
@@ -376,55 +438,61 @@ True re.sub(pattern, repl, string, count=0, flags=0) ``` -* simple substitutions -* `re.sub` will not change value of variable passed to it, has to be explicity assigned +* examples +* **Note** that as strings are immutable, `re.sub` will not change value of variable +passed to it, has to be explicity assigned ```python ->>> sentence = 'This is a sample string' ->>> re.sub('sample', 'test', sentence) -'This is a test string' - ->>> sentence -'This is a sample string' ->>> sentence = re.sub('sample', 'test', sentence) ->>> sentence -'This is a test string' - ->>> re.sub('/', '-', '25/06/2016') -'25-06-2016' ->>> re.sub('/', '-', '25/06/2016', count=1) -'25-06/2016' - ->>> greeting = '***** Have a great day *****' ->>> re.sub('\*', '=', greeting) -'===== Have a great day =====' +>>> ip_lines = "catapults\nconcatenate\ncat" +>>> print(re.sub(r'^', r'* ', ip_lines, flags=re.M)) +* catapults +* concatenate +* cat + +# replace 'par' only at start of word +>>> re.sub(r'\bpar', r'X', 'par spar apparent spare part') +'X spar apparent spare Xt' + +# same as: r'part|parrot|parent' +>>> re.sub(r'par(en|ro)?t', r'X', 'par part parrot parent') +'par X X X' + +# remove first two columns where : is delimiter +>>> re.sub(r'\A([^:]+:){2}', r'', 'foo:123:bar:baz', count=1) +'bar:baz' ``` * backreferencing ```python ->>> words = 'night and day' ->>> re.sub(r'(\w+)( \w+ )(\w+)', r'\3\2\1', words) -'day and night' - ->>> line = 'Can you spot the the mistakes? I i seem to not' ->>> re.sub(r'\b(\w+) \1\b', r'\1', line, flags=re.I) -'Can you spot the mistakes? I seem to not' +# remove any number of consecutive duplicate words separated by space +# quantifiers can be applied to backreferences too! +>>> re.sub(r'\b(\w+)( \1)+\b', r'\1', 'aa a a a 42 f_1 f_1 f_13.14') +'aa a 42 f_1 f_13.14' + +# add something around the matched strings +>>> re.sub(r'\d+', r'(\g<0>0)', '52 apples and 31 mangoes') +'(520) apples and (310) mangoes' + +# swap words that are separated by a comma +>>> re.sub(r'(\w+),(\w+)', r'\2,\1', 'a,b 42,24') +'b,a 24,42' ``` * using functions in replace part of `re.sub()` +* **Note** that Python version **3.7** is used here ```python ->>> import math +>>> from math import factorial >>> numbers = '1 2 3 4 5' - >>> def fact_num(n): -... return str(math.factorial(int(n.group(1)))) +... return str(factorial(int(n[0]))) ... ->>> re.sub(r'(\d+)', fact_num, numbers) +>>> re.sub(r'\d+', fact_num, numbers) '1 2 6 24 120' ->>> re.sub(r'(\d+)', lambda m: str(math.factorial(int(m.group(1)))), numbers) +# using lambda +>>> re.sub(r'\d+', lambda m: str(factorial(int(m[0]))), numbers) '1 2 6 24 120' ``` @@ -436,49 +504,45 @@ re.sub(pattern, repl, string, count=0, flags=0) ### Compiling Regular Expressions +* Regular expressions can be compiled using `re.compile` function, which gives back a +`re.Pattern` object +* The top level `re` module functions are all available as methods for this object +* Compiling a regular expression helps if the RE has to be used in multiple +places or called upon multiple times inside a loop (speed benefit) +* By default, Python maintains a small list of recently used RE, so the speed benefit +doesn't apply for trivial use cases + ```python ->>> swap_words = re.compile(r'(\w+)( \w+ )(\w+)') ->>> swap_words -re.compile('(\\w+)( \\w+ )(\\w+)') - ->>> words = 'night and day' - ->>> swap_words.search(words).group() -'night and day' ->>> swap_words.search(words).group(1) -'night' ->>> swap_words.search(words).group(2) -' and ' ->>> swap_words.search(words).group(3) -'day' ->>> swap_words.search(words).group(4) -Traceback (most recent call last): - File "", line 1, in -IndexError: no such group - ->>> bool(swap_words.search(words)) +>>> pet = re.compile(r'dog') +>>> type(pet) + +>>> bool(pet.search('They bought a dog')) True ->>> swap_words.findall(words) -[('night', ' and ', 'day')] +>>> bool(pet.search('A cat crossed their path')) +False ->>> swap_words.sub(r'\3\2\1', words) -'day and night' ->>> swap_words.sub(r'\3\2\1', 'yin and yang') -'yang and yin' +>>> remove_parentheses = re.compile(r'\([^)]*\)') +>>> remove_parentheses.sub('', 'a+b(addition) - foo() + c%d(#modulo)') +'a+b - foo + c%d' +>>> remove_parentheses.sub('', 'Hi there(greeting). Nice day(a(b)') +'Hi there. Nice day' ```
### Further Reading on Regular Expressions +* [Python re(gex)?](https://github.com/learnbyexample/py_regular_expressions) - a book on regular expressions * [Python docs - re module](https://docs.python.org/3/library/re.html) * [Python docs - introductory tutorial to using regular expressions](https://docs.python.org/3/howto/regex.html) -* [developers.google - Regular Expressions tutorial](https://developers.google.com/edu/python/regular-expressions) -* [automatetheboringstuff - Regular Expressions](https://automatetheboringstuff.com/chapter7/) * [Comprehensive reference: What does this regex mean?](https://stackoverflow.com/questions/22937618/reference-what-does-this-regex-mean) +* [rexegg](https://www.rexegg.com/) - tutorials, tricks and more +* [regular-expressions](https://www.regular-expressions.info/) - tutorials and tools +* [CommonRegex](https://github.com/madisonmay/CommonRegex) - collection of common regular expressions * Practice tools - * [online regex tester](https://regex101.com/#python) shows explanations, has reference guides and ability to save and share regex - * [regexone](http://regexone.com/) - interative tutorial + * [regex101](https://regex101.com/) - visual aid and online testing tool for regular expressions, select flavor as Python before use + * [debuggex](https://www.debuggex.com) - railroad diagrams for regular expressions, select flavor as Python before use + * [regexone](https://regexone.com/) - interative tutorial * [cheatsheet](https://www.shortcutfoo.com/app/dojos/python-regex/cheatsheet) - one can also learn it [interactively](https://www.shortcutfoo.com/app/dojos/python-regex) * [regexcrossword](https://regexcrossword.com/) - practice by solving crosswords, read 'How to play' section before you start diff --git a/exercise_files/f1.txt b/exercise_files/f1.txt new file mode 100644 index 0000000..d944f3c --- /dev/null +++ b/exercise_files/f1.txt @@ -0,0 +1,7 @@ +8 +53 +3.14 +84 +73e2 +100 +2937 diff --git a/exercise_files/f2.txt b/exercise_files/f2.txt new file mode 100644 index 0000000..cc8fa8c --- /dev/null +++ b/exercise_files/f2.txt @@ -0,0 +1,4 @@ +Hello123 World 35 +341 2 +Good 13day +How are 1784 you diff --git a/exercise_files/f3.txt b/exercise_files/f3.txt new file mode 100644 index 0000000..0794f6f --- /dev/null +++ b/exercise_files/f3.txt @@ -0,0 +1,8 @@ +power.Log +foo.123.txt +list +report_12.log +baz.TXT +hello.RB +loop.do.rb +Fav_books diff --git a/exercise_files/poem.txt b/exercise_files/poem.txt new file mode 100644 index 0000000..8eb2255 --- /dev/null +++ b/exercise_files/poem.txt @@ -0,0 +1,4 @@ +Roses are red, +Violets are blue, +Sugar is sweet, +And so are you. diff --git a/exercise_files/q2a_int_length.py b/exercise_files/q2a_int_length.py new file mode 100755 index 0000000..f8898f4 --- /dev/null +++ b/exercise_files/q2a_int_length.py @@ -0,0 +1,18 @@ +#!/usr/bin/python3 + +def len_int(n): + pass + +assert len_int(123) == 3 +assert len_int(2) == 1 +assert len_int(+42) == 2 +assert len_int(-42) == 2 +assert len_int(572342) == 6 +assert len_int(962306349871524124750813401378124) == 33 + +try: + len_int('a') +except TypeError as e: + assert str(e) == 'provide only integer input' + +print('all tests passed') diff --git a/exercise_files/q2b_str_comparison.py b/exercise_files/q2b_str_comparison.py new file mode 100755 index 0000000..f2cf54f --- /dev/null +++ b/exercise_files/q2b_str_comparison.py @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +def str_cmp(s1, s2): + pass + +assert str_cmp('abc', 'Abc') +assert str_cmp('Hi there', 'hi there') +assert not str_cmp('foo', 'food') +assert str_cmp('nice', 'nice') +assert str_cmp('GoOd DaY', 'gOOd dAy') +assert not str_cmp('how', 'who') + +print('all tests passed') diff --git a/exercise_files/q2c_str_same_letters.py b/exercise_files/q2c_str_same_letters.py new file mode 100755 index 0000000..15f3ec4 --- /dev/null +++ b/exercise_files/q2c_str_same_letters.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 + +def str_anagram(s1, s2): + pass + +assert str_anagram('god', 'Dog') +assert str_anagram('beat', 'abet') +assert str_anagram('Tap', 'paT') +assert not str_anagram('beat', 'table') +assert not str_anagram('seat', 'teal') + +print('all tests passed') diff --git a/exercise_files/q2d_to_num.py b/exercise_files/q2d_to_num.py new file mode 100644 index 0000000..11a270c --- /dev/null +++ b/exercise_files/q2d_to_num.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 + +def num(ip): + pass + +assert num(3) == 3 +assert num(0x1f) == 31 +assert num(0b101) == 5 +assert num(0o10) == 8 +assert num(3.32) == 3.32 +assert num('123') == 123 +assert num('-78') == -78 +assert num(" 42 \n ") == 42 +assert num('3.14') == 3.14 +assert num('3.982e5') == 398200.0 +s = '56' +assert num(s) + 44 == 100 +s = '8' * 10 +assert num(s) == 8888888888 + +assert type(num('42')) == int +assert type(num('1.23')) == float + +try: + assert num('foo') +except ValueError as e: + assert str(e) == 'could not convert string to int or float' + +try: + assert num(['1', '2.3']) +except TypeError as e: + assert str(e) == 'not a valid input' + +print('all tests passed') diff --git a/exercise_files/q3a_6by7.py b/exercise_files/q3a_6by7.py new file mode 100755 index 0000000..85b0f40 --- /dev/null +++ b/exercise_files/q3a_6by7.py @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +def six_by_seven(num): + pass + +assert six_by_seven(66) == 'Food' +assert six_by_seven(13) == 'Oops' +assert six_by_seven(42) == 'Universe' +assert six_by_seven(14) == 'Good' +assert six_by_seven(84) == 'Universe' +assert six_by_seven(235432) == 'Oops' + +print('all tests passed') diff --git a/exercise_files/q4a_iter_product.py b/exercise_files/q4a_iter_product.py new file mode 100755 index 0000000..867a9df --- /dev/null +++ b/exercise_files/q4a_iter_product.py @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +def product(ip_iterable): + pass + +assert product([1, 4, 21]) == 84 +assert product([-4, 2.3e12, 77.23, 982, 0b101]) == -3.48863356e+18 +assert product((-3, 11, 2)) == -66 +assert product({8, 300}) == 2400 +assert product([234, 121, 23, 945, 0]) == 0 +assert product(range(1, 6)) == 120 + +print('all tests passed') diff --git a/exercise_files/q4b_lowest_value.py b/exercise_files/q4b_lowest_value.py new file mode 100755 index 0000000..7db197c --- /dev/null +++ b/exercise_files/q4b_lowest_value.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 + +def nth_lowest(): + pass + +nums = [42, 23421341, 234.2e3, 21, 232, 12312, -2343] +assert nth_lowest(nums, 3) == 42 +assert nth_lowest(nums, 5) == 12312 + +nums = [1, -2, 4, 2, 1, 3, 3, 5] +assert nth_lowest(nums) == -2 +assert nth_lowest(nums, 4) == 3 + +assert nth_lowest('unrecognizable', 3) == 'c' + +print('all tests passed') diff --git a/exercise_files/q4c_word_slices.py b/exercise_files/q4c_word_slices.py new file mode 100755 index 0000000..562e6e7 --- /dev/null +++ b/exercise_files/q4c_word_slices.py @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +def word_slices(s): + pass + +assert word_slices('i') == ["i"] +assert word_slices('to') == ["to"] +assert word_slices('are') == ["ar", "are", "re"] +assert word_slices('boat') == ["bo", "boa", "boat", "oa", "oat", "at"] +assert word_slices('table') == ["ta", "tab", "tabl", "table", "ab", + "abl", "able", "bl", "ble", "le"] + +print('all tests passed') diff --git a/exercise_files/q5c_sort_by_ext.py b/exercise_files/q5c_sort_by_ext.py new file mode 100755 index 0000000..125ef2f --- /dev/null +++ b/exercise_files/q5c_sort_by_ext.py @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +def sort_by_ext(ip): + pass + +exp_op = ['Fav_books\n', 'list\n', 'power.Log\n', + 'report_12.log\n', 'hello.RB\n', 'loop.do.rb\n', + 'baz.TXT\n', 'foo.123.txt\n'] + +assert sort_by_ext('f3.txt') == exp_op + +print('test passed') + diff --git a/exercise_files/q6a_one_char_diff.py b/exercise_files/q6a_one_char_diff.py new file mode 100755 index 0000000..9f84508 --- /dev/null +++ b/exercise_files/q6a_one_char_diff.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +def is_one_char_diff(word1, word2): + pass + +assert is_one_char_diff('bar', 'car') +assert is_one_char_diff('bar', 'Bat') +assert is_one_char_diff('bar', 'bar') +assert is_one_char_diff('bar', 'baZ') +assert is_one_char_diff('A', 'b') + +assert not is_one_char_diff('a', '') +assert not is_one_char_diff('bar', 'bark') +assert not is_one_char_diff('bar', 'Art') +assert not is_one_char_diff('bar', 'bot') +assert not is_one_char_diff('ab', '') + +assert is_one_char_diff('Food', 'good') +assert is_one_char_diff('food', 'fold') +assert not is_one_char_diff('food', 'Foody') +assert not is_one_char_diff('food', 'fled') + +print('all tests passed') diff --git a/exercise_files/q6b_alpha_order.py b/exercise_files/q6b_alpha_order.py new file mode 100755 index 0000000..197886b --- /dev/null +++ b/exercise_files/q6b_alpha_order.py @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +def is_alpha_order(word): + pass + +assert is_alpha_order('bot') +assert is_alpha_order('art') +assert is_alpha_order('toe') +assert is_alpha_order('AborT') + +assert not is_alpha_order('are') +assert not is_alpha_order('boat') +assert not is_alpha_order('Flee') + +#sentence +def is_alpha_order_sentence(sentence): + pass + +assert is_alpha_order_sentence('Toe got bit') +assert not is_alpha_order_sentence('All is well') + +print('all tests passed') diff --git a/exercise_files/q6c_max_nested.py b/exercise_files/q6c_max_nested.py new file mode 100755 index 0000000..13be372 --- /dev/null +++ b/exercise_files/q6c_max_nested.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 + +def max_nested_braces(expr): + pass + +assert max_nested_braces('a*b') == 0 +assert max_nested_braces('a*b{') == -1 +assert max_nested_braces('a*{b+c}') == 1 +assert max_nested_braces('{a+2}*{b+c}') == 1 +assert max_nested_braces('a*{b+c*{e*3.14}}') == 2 +assert max_nested_braces('a*{b+c*{e*3.14}}}') == -1 +assert max_nested_braces('a*{b+c}}') == -1 +assert max_nested_braces('a*b+{}') == 1 +assert max_nested_braces('}a+b{') == -1 +assert max_nested_braces('{{a+2}*{b+c}+e}') == 2 +assert max_nested_braces('{{a+2}*{b+{c*d}}+e}') == 3 +assert max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') == 4 +assert max_nested_braces('{{a+2}*{{b}+{c*d}}+e*d}}') == -1 + +print('all tests passed') diff --git a/exercise_files/q7c_longest_word.py b/exercise_files/q7c_longest_word.py new file mode 100755 index 0000000..24cec7d --- /dev/null +++ b/exercise_files/q7c_longest_word.py @@ -0,0 +1,14 @@ +#!/usr/bin/python3 + +def longest_word(): + pass + +ip_path = 'poem.txt' +assert longest_word(ip_path) == 'Violets' + +# The Scarlet Pimpernel +ip_path = 'https://www.gutenberg.org/files/60/60.txt' +assert longest_word(ip_path, True) == 'misunderstandings' + +print('all tests passed') + diff --git a/exercise_solutions/f1.txt b/exercise_solutions/f1.txt new file mode 100644 index 0000000..d944f3c --- /dev/null +++ b/exercise_solutions/f1.txt @@ -0,0 +1,7 @@ +8 +53 +3.14 +84 +73e2 +100 +2937 diff --git a/exercise_solutions/f2.txt b/exercise_solutions/f2.txt new file mode 100644 index 0000000..cc8fa8c --- /dev/null +++ b/exercise_solutions/f2.txt @@ -0,0 +1,4 @@ +Hello123 World 35 +341 2 +Good 13day +How are 1784 you diff --git a/exercise_solutions/f3.txt b/exercise_solutions/f3.txt new file mode 100644 index 0000000..0794f6f --- /dev/null +++ b/exercise_solutions/f3.txt @@ -0,0 +1,8 @@ +power.Log +foo.123.txt +list +report_12.log +baz.TXT +hello.RB +loop.do.rb +Fav_books diff --git a/exercise_solutions/poem.txt b/exercise_solutions/poem.txt new file mode 100644 index 0000000..8eb2255 --- /dev/null +++ b/exercise_solutions/poem.txt @@ -0,0 +1,4 @@ +Roses are red, +Violets are blue, +Sugar is sweet, +And so are you. diff --git a/exercise_solutions/q1a_usr_ip.py b/exercise_solutions/q1a_usr_ip.py new file mode 100755 index 0000000..267573f --- /dev/null +++ b/exercise_solutions/q1a_usr_ip.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +print('Please provide the following details') +name = input('Enter your name: ') +dept = input('Enter your department: ') +colg = input('Enter your college: ') + +op_fmt = '{:<11}: {}' + +print('\n------------------------------------') +print(op_fmt.format('Name', name)) +print(op_fmt.format('Department', dept)) +print(op_fmt.format('College', colg)) + + +####### Alternate +#print('Please provide the following details') +#labels = ('Name', 'Department', 'College') +#usr_details = [input('Enter your ' + itm + ': ') for itm in labels] +# +#itm_size = len(sorted(labels, key=len)[-1]) + 1 +#op_fmt = '{:<' + str(itm_size) + '}: {}' +#print('\n------------------------------------') +#for k,v in zip(labels, usr_details): +# print(op_fmt.format(k, v)) diff --git a/exercise_solutions/q2a_int_length.py b/exercise_solutions/q2a_int_length.py new file mode 100755 index 0000000..99a1351 --- /dev/null +++ b/exercise_solutions/q2a_int_length.py @@ -0,0 +1,22 @@ +#!/usr/bin/python3 + +def len_int(n): + if type(n) != int: + raise TypeError('provide only integer input') + + str_n = str(abs(n)) + return len(str_n) + +assert len_int(123) == 3 +assert len_int(2) == 1 +assert len_int(+42) == 2 +assert len_int(-42) == 2 +assert len_int(572342) == 6 +assert len_int(962306349871524124750813401378124) == 33 + +try: + len_int('a') +except TypeError as e: + assert str(e) == 'provide only integer input' + +print('all tests passed') diff --git a/exercise_solutions/q2b_str_comparison.py b/exercise_solutions/q2b_str_comparison.py new file mode 100755 index 0000000..fd8923c --- /dev/null +++ b/exercise_solutions/q2b_str_comparison.py @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +def str_cmp(s1, s2): + return s1.lower() == s2.lower() + +assert str_cmp('abc', 'Abc') +assert str_cmp('Hi there', 'hi there') +assert not str_cmp('foo', 'food') +assert str_cmp('nice', 'nice') +assert str_cmp('GoOd DaY', 'gOOd dAy') +assert not str_cmp('how', 'who') + +print('all tests passed') diff --git a/exercise_solutions/q2c_str_same_letters.py b/exercise_solutions/q2c_str_same_letters.py new file mode 100755 index 0000000..cbca962 --- /dev/null +++ b/exercise_solutions/q2c_str_same_letters.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 + +def str_anagram(s1, s2): + return sorted(s1.lower()) == sorted(s2.lower()) + +assert str_anagram('god', 'Dog') +assert str_anagram('beat', 'abet') +assert str_anagram('Tap', 'paT') +assert not str_anagram('beat', 'table') +assert not str_anagram('seat', 'teal') + +print('all tests passed') diff --git a/exercise_solutions/q2d_to_num.py b/exercise_solutions/q2d_to_num.py new file mode 100755 index 0000000..d840f22 --- /dev/null +++ b/exercise_solutions/q2d_to_num.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 + +def num(ip): + if type(ip) in (int, float): + return ip + elif type(ip) != str: + raise TypeError('not a valid input') + + try: + return int(ip) + except ValueError: + try: + return float(ip) + except ValueError: + raise ValueError('could not convert string to int or float') + +assert num(3) == 3 +assert num(0x1f) == 31 +assert num(0b101) == 5 +assert num(0o10) == 8 +assert num(3.32) == 3.32 +assert num('123') == 123 +assert num('-78') == -78 +assert num(" 42 \n ") == 42 +assert num('3.14') == 3.14 +assert num('3.982e5') == 398200.0 +s = '56' +assert num(s) + 44 == 100 +s = '8' * 10 +assert num(s) == 8888888888 + +assert type(num('42')) == int +assert type(num('1.23')) == float + +try: + num('foo') +except ValueError as e: + assert str(e) == 'could not convert string to int or float' + +try: + num(['1', '2.3']) +except TypeError as e: + assert str(e) == 'not a valid input' + +print('all tests passed') diff --git a/exercise_solutions/q3a_6by7.py b/exercise_solutions/q3a_6by7.py new file mode 100755 index 0000000..557cd47 --- /dev/null +++ b/exercise_solutions/q3a_6by7.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +def six_by_seven(num): + if num % 42 == 0: + return 'Universe' + elif num % 7 == 0: + return 'Good' + elif num % 6 == 0: + return 'Food' + else: + return 'Oops' + +assert six_by_seven(66) == 'Food' +assert six_by_seven(13) == 'Oops' +assert six_by_seven(42) == 'Universe' +assert six_by_seven(14) == 'Good' +assert six_by_seven(84) == 'Universe' +assert six_by_seven(235432) == 'Oops' + +print('all tests passed') + +## bonus +#for num in range(1, 101): +# print(num, six_by_seven(num)) diff --git a/exercise_solutions/q3b_dec_bin.py b/exercise_solutions/q3b_dec_bin.py new file mode 100755 index 0000000..3349154 --- /dev/null +++ b/exercise_solutions/q3b_dec_bin.py @@ -0,0 +1,18 @@ +#!/usr/bin/python3 + +for n in range(1, 1001): + dec_n = str(n) + bin_n = format(n, 'b') + if dec_n == dec_n[::-1] and bin_n == bin_n[::-1]: + print(dec_n, bin_n) + + #oct_n = format(n, 'o') + #if dec_n == dec_n[::-1] and bin_n == bin_n[::-1] and oct_n == oct_n[::-1]: + # print('{0:d} {0:#b} {0:#o}'.format(n)) + + #oct_n = format(n, 'o') + #hex_n = format(n, 'x') + ##if all((dec_n == dec_n[::-1], bin_n == bin_n[::-1], oct_n == oct_n[::-1], hex_n == hex_n[::-1])): + #if dec_n == dec_n[::-1] and bin_n == bin_n[::-1] and \ + # oct_n == oct_n[::-1] and hex_n == hex_n[::-1]: + # print('{0:d} {0:#b} {0:#o} {0:#x}'.format(n)) diff --git a/exercise_solutions/q4a_iter_product.py b/exercise_solutions/q4a_iter_product.py new file mode 100755 index 0000000..84aa33c --- /dev/null +++ b/exercise_solutions/q4a_iter_product.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 + +def product(ip_iterable): + op = 1 + for n in ip_iterable: + op *= n + return op + +assert product([1, 4, 21]) == 84 +assert product([-4, 2.3e12, 77.23, 982, 0b101]) == -3.48863356e+18 +assert product((-3, 11, 2)) == -66 +assert product({8, 300}) == 2400 +assert product([234, 121, 23, 945, 0]) == 0 +assert product(range(1, 6)) == 120 + +print('all tests passed') diff --git a/exercise_solutions/q4b_lowest_value.py b/exercise_solutions/q4b_lowest_value.py new file mode 100755 index 0000000..7850568 --- /dev/null +++ b/exercise_solutions/q4b_lowest_value.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 + +def nth_lowest(ip_iterable, n=1): + return sorted(set(ip_iterable))[n-1] + +nums = [42, 23421341, 234.2e3, 21, 232, 12312, -2343] +assert nth_lowest(nums, 3) == 42 +assert nth_lowest(nums, 5) == 12312 + +nums = [1, -2, 4, 2, 1, 3, 3, 5] +assert nth_lowest(nums) == -2 +assert nth_lowest(nums, 4) == 3 + +assert nth_lowest('unrecognizable', 3) == 'c' + +print('all tests passed') diff --git a/exercise_solutions/q4c_word_slices.py b/exercise_solutions/q4c_word_slices.py new file mode 100755 index 0000000..77dae84 --- /dev/null +++ b/exercise_solutions/q4c_word_slices.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 + +def word_slices(s): + size = len(s) + if size < 3: + return [s] + return [s[i:j+1] for i in range(size-1) for j in range(i+1, size)] + +assert word_slices('i') == ["i"] +assert word_slices('to') == ["to"] +assert word_slices('are') == ["ar", "are", "re"] +assert word_slices('boat') == ["bo", "boa", "boat", "oa", "oat", "at"] +assert word_slices('table') == ["ta", "tab", "tabl", "table", "ab", + "abl", "able", "bl", "ble", "le"] + +print('all tests passed') diff --git a/exercise_solutions/q5a_col_sum.py b/exercise_solutions/q5a_col_sum.py new file mode 100755 index 0000000..cefbf05 --- /dev/null +++ b/exercise_solutions/q5a_col_sum.py @@ -0,0 +1,13 @@ +#!/usr/bin/python3 + +with open('f1.txt', 'r', encoding='ascii') as f: + total = 0 + for line in f: + total += float(line) + + assert total == 10485.14 + +print('test passed') + +# for small files that can fit in memory +#total = sum(float(n) for n in open('f1.txt', encoding='ascii').readlines()) diff --git a/exercise_solutions/q5b_sum_ints.py b/exercise_solutions/q5b_sum_ints.py new file mode 100755 index 0000000..9050aad --- /dev/null +++ b/exercise_solutions/q5b_sum_ints.py @@ -0,0 +1,14 @@ +#!/usr/bin/python3 + +import re + +with open('f2.txt', 'r', encoding='ascii') as f: + total = 0 + for line in f: + total += sum(int(n) for n in re.findall(r'\d+', line)) + + assert total == 2298 + +#assert sum(int(n) for n in re.findall(r'\d+', open('f2.txt', encoding='ascii').read())) == 2298 + +print('test passed') diff --git a/exercise_solutions/q5c_sort_by_ext.py b/exercise_solutions/q5c_sort_by_ext.py new file mode 100755 index 0000000..208442c --- /dev/null +++ b/exercise_solutions/q5c_sort_by_ext.py @@ -0,0 +1,14 @@ +#!/usr/bin/python3 + +def sort_by_ext(ip): + lines = open(ip, encoding='ascii').readlines() + return sorted(lines, key=lambda x: (x.split('.')[-1].lower(), x.lower())) + +exp_op = ['Fav_books\n', 'list\n', 'power.Log\n', + 'report_12.log\n', 'hello.RB\n', 'loop.do.rb\n', + 'baz.TXT\n', 'foo.123.txt\n'] + +assert sort_by_ext('f3.txt') == exp_op + +print('test passed') + diff --git a/exercise_solutions/q6a_one_char_diff.py b/exercise_solutions/q6a_one_char_diff.py new file mode 100755 index 0000000..9cb26a5 --- /dev/null +++ b/exercise_solutions/q6a_one_char_diff.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 + +def is_one_char_diff(word1, word2): + if len(word1) != len(word2): + return False + + word1, word2 = word1.lower(), word2.lower() + for i in range(len(word1)): + if word1[0:i] + word1[i+1:] == word2[0:i] + word2[i+1:]: + return True + + return False + +assert is_one_char_diff('bar', 'car') +assert is_one_char_diff('bar', 'Bat') +assert is_one_char_diff('bar', 'bar') +assert is_one_char_diff('bar', 'baZ') +assert is_one_char_diff('A', 'b') + +assert not is_one_char_diff('a', '') +assert not is_one_char_diff('bar', 'bark') +assert not is_one_char_diff('bar', 'Art') +assert not is_one_char_diff('bar', 'bot') +assert not is_one_char_diff('ab', '') + +assert is_one_char_diff('Food', 'good') +assert is_one_char_diff('food', 'fold') +assert not is_one_char_diff('food', 'Foody') +assert not is_one_char_diff('food', 'fled') + +print('all tests passed') diff --git a/exercise_solutions/q6b_alpha_order.py b/exercise_solutions/q6b_alpha_order.py new file mode 100755 index 0000000..5badf74 --- /dev/null +++ b/exercise_solutions/q6b_alpha_order.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +def is_alpha_order(word): + word = word.lower() + return list(word) == sorted(word) or list(word) == sorted(word, reverse=True) + +assert is_alpha_order('bot') +assert is_alpha_order('art') +assert is_alpha_order('toe') +assert is_alpha_order('AborT') + +assert not is_alpha_order('are') +assert not is_alpha_order('boat') +assert not is_alpha_order('Flee') + +# sentence +def is_alpha_order_sentence(sentence): + return all(is_alpha_order(word) for word in sentence.split()) + +assert is_alpha_order_sentence('Toe got bit') +assert not is_alpha_order_sentence('All is well') + +print('all tests passed') diff --git a/exercise_solutions/q6c_max_nested.py b/exercise_solutions/q6c_max_nested.py new file mode 100755 index 0000000..b022a79 --- /dev/null +++ b/exercise_solutions/q6c_max_nested.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 + +def max_nested_braces(expr): + max_count = count = 0 + for char in expr: + if char == '{': + count += 1 + if count > max_count: + max_count = count + elif char == '}': + if count == 0: + return -1 + count -= 1 + + if count != 0: + return -1 + return max_count + +assert max_nested_braces('a*b') == 0 +assert max_nested_braces('a*b{') == -1 +assert max_nested_braces('a*{b+c}') == 1 +assert max_nested_braces('{a+2}*{b+c}') == 1 +assert max_nested_braces('a*{b+c*{e*3.14}}') == 2 +assert max_nested_braces('a*{b+c*{e*3.14}}}') == -1 +assert max_nested_braces('a*{b+c}}') == -1 +assert max_nested_braces('a*b+{}') == 1 +assert max_nested_braces('}a+b{') == -1 +assert max_nested_braces('{{a+2}*{b+c}+e}') == 2 +assert max_nested_braces('{{a+2}*{b+{c*d}}+e}') == 3 +assert max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') == 4 +assert max_nested_braces('{{a+2}*{{b}+{c*d}}+e*d}}') == -1 + +print('all tests passed') + + +#### alternate using regular expressions + +#import re +# +#def max_nested_braces(expr): +# count = 0 +# while True: +# expr, no_of_subs = re.subn(r'\{[^{}]*\}', '', expr) +# if no_of_subs == 0: +# break +# count += 1 +# +# if re.search(r'[{}]', expr): +# return -1 +# return count + +# for bonus, use + instead of * +#assert max_nested_braces('a*b+{}') == -1 +#assert max_nested_braces('a*{b+{}+c*{e*3.14}}') == -1 diff --git a/exercise_solutions/q7_misc.py b/exercise_solutions/q7_misc.py new file mode 100755 index 0000000..f59d22f --- /dev/null +++ b/exercise_solutions/q7_misc.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 + +import subprocess +# provide path to your favorite media player and file to play +subprocess.call(['vlc', r'/path/to/file']) + +import webbrowser +webbrowser.open(r'https://github.com/learnbyexample/Python_Basics') diff --git a/exercise_solutions/q7c_longest_word.py b/exercise_solutions/q7c_longest_word.py new file mode 100755 index 0000000..6286b2e --- /dev/null +++ b/exercise_solutions/q7c_longest_word.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 + +import urllib.request, re + +def longest_word(ip, url=False): + if url: + ip_data = urllib.request.urlopen(ip).read().decode('utf-8') + else: + ip_data = open(ip, encoding='utf-8').read() + + return sorted(re.findall(r'[a-zA-Z]+', ip_data), key=len)[-1] + +ip_path = 'poem.txt' +assert longest_word(ip_path) == 'Violets' + +# The Scarlet Pimpernel +ip_path = 'https://www.gutenberg.org/files/60/60.txt' +assert longest_word(ip_path, True) == 'misunderstandings' + +print('all tests passed') + diff --git a/mini_projects/pcalc.py b/mini_projects/pcalc.py new file mode 100644 index 0000000..3b0a50b --- /dev/null +++ b/mini_projects/pcalc.py @@ -0,0 +1,55 @@ +#!/usr/bin/python3 + +""" +eval is used, so use at your own risk + +Examples: +$ ./pcalc.py -vx '0b101 + 3' +0b101 + 3 = 0x8 +$ ./pcalc.py '0x23' +35 +$ ./pcalc.py -f2 '76/13' +5.85 +$ ./pcalc.py '27**12' +150094635296999121 +$ echo '97 + 232' | ./pcalc.py +329 + +$ ./pcalc.py '42 + 2s' +Error: Not a valid input expression +""" + +import argparse, sys, fileinput + +parser = argparse.ArgumentParser() +parser.add_argument('arith_expr', nargs='?', default=sys.stdin, help="arithmetic expression") +parser.add_argument('-v', help="verbose, show both input and output in result", action="store_true") +parser.add_argument('-f', type=int, help="specify floating point output precision") +parser.add_argument('-b', help="output in binary format", action="store_true") +parser.add_argument('-o', help="output in octal format", action="store_true") +parser.add_argument('-x', help="output in hexadecimal format", action="store_true") +args = parser.parse_args() + +if type(args.arith_expr) != str: + args.arith_expr = fileinput.input().readline().strip() +ip_expr = args.arith_expr + +try: + result = eval(ip_expr) + + if args.f: + result = "{0:.{1}f}".format(result, args.f) + elif args.b: + result = "{:#b}".format(int(result)) + elif args.o: + result = "{:#o}".format(int(result)) + elif args.x: + result = "{:#x}".format(int(result)) + + if args.v: + print("{} = {}".format(args.arith_expr, result)) + else: + print(result) +except (NameError, SyntaxError) as e: + sys.exit("Error: Not a valid input expression") +