• Getters and setters are not needed
  • There is always direct attribute access
  • Use properties or decorators if you really need to intercept attribute access
  • Abuse of @staticmethod and @classmethod
    • @staticmethod has no sense. Only justification is organization of code You can define functions and attributes directly in modules
      • @classmethod only used in constructors. Try to avoid class methods and attributes
  • Abuse of @staticmethod and @classmethod
    • @staticmethod has no sense. Only justification is organization of code You can define functions and attributes directly in modules
      • @classmethod only used in constructors. Try to avoid class methods and attributes
  • Define only one class per module
    • You can define several "helper" or internal classes in the same module
    • And even functions and attributes
  • Fear to multiple inheritance
    • Mixins and multiple inheritance are widely used in Python
    • You must understand what you are implementing, that's your work
    • "we're all grown-ups here"
  • Abuse of type checking
    • Duck typing and EAFP (It is Easier to Ask for Forgiveness than Permission)
    • Get used to the "uncertainity" of the exact type of your arguments

In [ ]:
a = []
for i in range(10):
    a.append(i*i)

a = [i*i for i in range(10)]

In [ ]:
with open("file.txt", "w") as f:
    f.write("nee\n")
    f.write("arms\n")

[line.replace("spam","eggs") for line in open("file.txt") if line.startswith("nee")]

In [ ]:
# Functions are first class objects
def int_validator(param):
    if isinstance(param, int):
        print "Int value"
    else:
        print "Not int value"

def str_validator(param):
    if isinstance(param, str):
        print "Str value"
    else:
        print "Not str value"

def validate(param, validators):
    for validator in validators:
        validator(param)
        
validate(34, [int_validator, str_validator])
validate("34", [int_validator, str_validator])

In [ ]:
# properties
class Results(object):
    _long_time_property = None
    @property
    def long_time_property(self):
        if not _long_time_property:
            _long_time_property = long_time_operation()
        return _long_time_property
    
    def long_time_operation(self):
        time.sleep(2)
        return 4

In [ ]:
r = Results()
r.long_time_property
print "First calculation"

In [ ]:
r.long_time_property
print "second calculation"

In [ ]:
#Default and keyword arguments.
def my_func(arg1, arg2=2, arg3=3, arg4=4):
    return arg1 ** arg2 + arg3 ** arg4

print my_func(3, arg3=2) # Use keyword arguments to call skip some of the arguments with default value

# Better than multiple constructors ?

In [ ]:
def my_func(arg1=1, arg2=2, **kwargs):  # This arbitrary 'args' list is a (kind-off) tuple of positional arguments
    print kwargs
    return arg1 + arg2

my_func(2, 3)

my_func(2, 3, param3=5, param4=7)

In [ ]:
spam = {"param3": 5, "param4": 7}
my_func(2, 3, **spam) # It is possible to unpack a tuple or list as an arbitrary list of arguments

In [ ]:
def nee():
    return 1
spam = nee()
print spam

In [ ]:
# refactor
def nee():
    return 1, "uno"
spam, eggs = nee()
print spam
print eggs

In [ ]:
# Built-in syntax for lists and dictionaries.
# See basic module