# Advanced Python 🐍
###### tags: `yotta` `python`
[TOC]
## Project Setup & management
### Install Miniconda
#### Mac
```bash
brew install miniconda
# adds conda installation in your PATH
conda init bash
```
#### Linux (Ubuntu)
```bash
# you might need to sudo these commands
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
chmod +x Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh -p $HOME/miniconda3
```
### Create your Conda environment
Why use virtual environment ?
- To separate any python install that is already on your system with the ones that you will be using on your projects
- reproducibility
- completely isolate your projects from one another
```bash
# create a new virtual environment named 'myenv'
# with the latest python 3 available
conda create --name myenv python=3
# activate it
conda activate myenv
# you can see that the python which is called
# within this environment is the one installed with it
which python3
# /.../miniconda/base/envs/myenv/bin/python3
which pip
# /.../miniconda/base/envs/myenv/bin/pip
# to see its version
python --version
# to get info on your current environment
# (version, location...)
conda info
# to list all virtual environments on your machine
conda info --envs
# to deactivate your current environment
conda deactivate
# to delete your environment 'myenv'
conda remove --name myenv --all
````
### Install packages
```bash
# 2 ways to do it
pip install pandas
conda install pandas
# to see what packages are in your current environment
# (and which versions)
conda list
```
### Alternative: venv
```bash
# create virtual environment myenv
python -m venv .myenv
# activate myenv
source .myenv/bin/activate
# check wich version are used
which python
which pip
# deactivate current environment
deactivate
```
### misc.
```python
# In the body of the script, after imports, def...
if __name__ == '__main__':
# __name__ is a context variable which is
# '__main__' if called directly (not via import)
# Split current line : \ (to be avoided)
# When you need a loop but don't need the actual counter variable
for _ in range(n):
...
```
### lambda
```python
# Bad usage example of a lambda
is_curzon = lambda n: not (1 + 2**n) % (1 + 2*n)
# Right usage example for a lambda
def apply(func):
x = 2
func(x)
apply(lambda x: x*2)
# Another exemple:
def mutate_dict(f,d):
for k, v in d.iteritems():
d[k] = f(v)
my_dictionary = {'a':1, 'b':2}
mutate_dict(lambda x: x+1, my_dictionary)
```
### generators
```python
# Enumerate : generator, generates tuple on the fly
enumerate("Hello")
# Useful for cases such as:
for i, char in enumerate(message):
...
# i is going to be the number (position) and
# char the actual character at that positon
# To store it in memory (i.e. store it into an iterator)
list(enumerate("Hello"))
set(enumerate("Hello"))
# If you need to iterate on multiple iterators at the same time :
number_list = [1, 2, 3]
str_list = ['one', 'two', 'three', 'four']
for x, y in zip(number_list, str_list):
print(x, x)
# Stops at the shortest iterator, here number_list (3)
# Works with more than 2 iterators
```