.. default-role:: literal .. default-domain: python Typage, types de base ====================== python est un langage dynamiquement typé. qu'est-ce que cela signifie ? - pas de déclaration des types (attention différent de l'inférence de types): les variables n'ont pas de type fixé - pas de déclaration des variables (une variable est créée au moyen de la première affectation) .. todo:: créer une variable >>> a = 3 >>> a 3 .. todo:: - ouvrir l'interpréteur python - dans la console créer un objet de type integer, float, string, liste, dictionnaire - vérifier les types à l'aide de la fonction `type()` - vérifier que en python tout est objet - type de base et types conteneurs - types mutables et types immutables >>> a = 2 >>> b = 3 >>> b = 5 >>> a 2 >>> b 5 >>> l = ['a', 'b', 'c', 'd'] >>> p = [l, 'e'] >>> p [['a', 'b', 'c', 'd'], 'e'] >>> l = ['i', 'j'] >>> p [['a', 'b', 'c', 'd'], 'e'] >>> l ['i', 'j'] .. todo:: jouer avec les types - l'interpréteur python comme calculette, les optérations numériques >>> x = 1 >>> y =2 >>> x > y False >>> x == y False >>> x != y True - l'interpréteur pour manipuler les strings >>> s = "bla bla" 'bla bla' >>> s = 'bla bla' 'bla bla' >>> s = " 'bli bli' " >>> s " 'bli bli' " >>> s = """ sdfsdf "bla bla" sdfsdf""" >>> s ' sdfsdf "bla bla" sdfsdf' >>> >>> s = "bla bla" >>> m = "hu hu" >>> s + m 'bla blahu hu' >>> m * 3 'hu huhu huhu hu' >>> >>> len(m) 5 >>> >>> m[3] 'h' >>> m[3:5] 'hu' >>> >>> s= 'a' >>> dir(s) ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] >>> t = "abc" >>> t.startswith("a") True >>> l = ['a', 'b', 'c'] >>> "-".join(l) 'a-b-c' >>> .. todo:: lower(), upper(), strip(), title() .. todo: recherche et remplacement dans une string index(), find(), replace() - le module :mod:`regexp` .. module:: regexp :synopsis: expression régulières >>> import re >>> s = "sdf dfdfdf blah bla df" >>> re.findall(r'\w*', s) ['sdf', '', 'dfdfdf', '', 'blah', '', 'bla', '', 'df', ''] >>> re.findall(r'df*', s) ['df', 'df', 'df', 'df', 'df'] >>> - enlever les accents >>> s = u"un été même pas chaud" >>> import unicodedata as U >>> s2 = ''.join(U.normalize('NFD', x)[0] for x in s) >>> s2 u'un ete meme pas chaud' - enlever la ponctuation >>> import re >>> import string >>> rgx = re.compile('[%s]' % string.punctuation) >>> string.punctuation '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' - l'encodage (unicode et la compression utf-8): >>> u = u"éèà bla" >>> u u'\xe9\xe8\xe0 bla' >>> u.encode("utf-8") '\xc3\xa9\xc3\xa8\xc3\xa0 bla' >>> print u.encode("utf-8") éèà bla manips importantes de traitement unicode (si on n'est pas en python 3) >>> u = u"ésdsfè" >>> u u'\xe9sdsf\xe8' >>> print u ésdsfè >>> str(u) Traceback (most recent call last): File "", line 1, in UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128) >>> u.decode("utf-8") Traceback (most recent call last): File "", line 1, in File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode return codecs.utf_8_decode(input, errors, True) UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128) >>> u.encode("utf-8") '\xc3\xa9sdsf\xc3\xa8' >>> s = u.encode("utf-8") >>> type(s) # il faut utiliser .encode(), et pas .decode()... if type(s) == unicode #types.UnicodeType: bla bla if type(s) == str: rien à faire - tuples, listes, dictionnaires >>> t = (1,2,3) >>> l = [1,2,3] >>> d = {'a': 2, 'b':3, 'c':4} >>> >>> l = ['e','p','q','t'] >>> l.pop() 't' >>> l ['e', 'p', 'q'] >>> l ['e', 'p', 'q'] >>> l.pop(1) 'p' >>> l ['e', 'q'] >>> .. important:: utiliser get plutôt que l'accès par items lorsque l'on n'est pas sûr :: >>> d = {} >>> d.get('toto') >>> d['toto'] ='titi' >>> d.get('toto') 'titi' >>> print d.get('toto') titi >>> print d.get('to') None >>> d['tot'] Traceback (most recent call last): File "", line 1, in KeyError: 'tot' >>> KeyboardInterrupt >>> >>> x=dict(a=23,b=45,c=67,d=89) >>> x {'a': 23, 'c': 67, 'b': 45, 'd': 89} >>> y=dict.fromkeys(range(4), 'ho') >>> y {0: 'ho', 1: 'ho', 2: 'ho', 3: 'ho'} # and a new method to fetch-and-remove an item, by key: >>> x.pop('c') 67 >>> x {'a': 23, 'b': 45, 'd': 89} >>> x.pop('z') Traceback (most recent call last): File "", line 1, in ? KeyError: 'z' >>> x.pop('z', 55) 55 >>> for x in enumerate('ciao'): print x ... (0, 'c') (1, 'i') (2, 'a') (3, 'o') >>> .. module:: collections :synopsis: autres types de données - le module :mod:`collections` : - `OrderedDict`, `defaultdict` remarques sur les tuples >>> 1, (1,) >>> (1,) (1,) >>> (1) 1 >>> () () >>> tuple() () >>> value = 1, >>> value (1,) .. todo:: addition de listes, append, tranches, tri de listes :: def remove_duplicates(lst): ... l = [4, 7, 30, "hello", 7, "world", 30, 7] remove_duplicates(l) assert l == [4, 7, 30, "hello", "world"] .. todo:: defaultdict, get (avec une valeur par défaut) - tri de liste personnalisé : .. literalinclude:: snippets/sorter.py >>> 'a' in d True >>> t[1] 2 >>> >>> for i in l: ... print i ... 1 2 3 >>> >>> for i in d: ... print i ... a c b >>> >>> for key, value in d.items(): ... print key, " : ", value ... a : 2 c : 4 b : 3 >>> .. todo:: l'interpréteur python pour l'introspection des objets en python, **pas besoin de déclaration de type**. Qu'est-ce que ça veut dire ? >>> type(1) >>> >>> dir(3) ['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real'] >>> >>> type(3) >>> - Le type set Set collection non ordonnées d'éléments uniques - le type set immutable et le type set mutable. - un ensemble peut être étendu ou bien seulement englobé :: Manipulations non supportées >>> e["a"] Traceback (most recent call last): TypeError: unsubscriptable object >>> e.append("a") Traceback (most recent call last): AttributeError: 'Set' object has no attribute 'append' Création Ensemble vide : :: >>> import sets >>> e = sets.Set() >>> e Set([]) >>> import sets >>> e = sets.Set("toto") >>> e Set(['t', 'o']) >>> d = {"a":1,"b":2} >>> f = sets.Set(d) >>> f Set(['a', 'b']) Appartenance et définition en extension :: Set(['a', 'b']) >>> "a" in i True >>> len(h) 4 >>> for u in h: ... print u ... a b t o Ajout :
    >>> e.add("a")
    >>> e
    Set(['a'])
    >>> e.add("b", "c")
    Traceback (most recent call last):
    TypeError: add() takes exactly 2 arguments (3 given)
    >>> for i in range(5):
    ...   e.add(i)
    ...
    >>> e
    Set(['a', 0, 2, 3, 4, 1])
    Set(['a'])
    >>> e.add("b", "c")
    Traceback (most recent call last):
    TypeError: add() takes exactly 2 arguments (3 given)
    >>> for i in range(5):
    ...   e.add(i)
    ...
    >>> e
    # Suppression :
    
    >>> f = e.copy()
    >>> f
    Set(['a', 0, 2, 3, 4, 1])
    >>> f.remove(0)

Opérations sur les ensembles

::

    >>> v = sets.Set()

    >>> e.issubset(f)
    False
    >>> f.issubset(e)
    True
    >>> e.issuperset(f)
    True
    >>> f < e
    True
    >>> f > e
    False
    >>> e
    Set(['a', 0, 2, 3, 4, 1, ImmutableSet(['p'])])
    >>> e.copy()
    Set(['a', 0, 2, 3, 4, 1, ImmutableSet(['p'])])
    >>> e.remove(3)
    >>> e
    Set(['a', 0, 2, 4, 1, ImmutableSet(['p'])])
    >>> 0 in e
    True
    >>> e.pop()
    'a'
    >>> e
    Set([0, 2, 4, 1, ImmutableSet(['p'])])
    >>> e.pop()
    0
    >>> e.discard(4)
    >>> e
    Set([2, 1, ImmutableSet(['p'])])
    >>> e.discard("p")
    >>> e
    Set([2, 1, ImmutableSet(['p'])])
    >>> e.discard(sets.Set("p"))
    >>> e
    Set([2, 1])
    >>>
    >>> e -= f
    >>> e
    Set([])
    >>> f
    Set(['a', 2, 3, 4, 1])
    >>>
    >>> e.clear()
    >>> e
    Set([])
    >>>
    # copy
    >>> h == e
    False
    >>> h != e
    True
    >>> u = h.copy()
    >>> u
    Set(['a', 'b', 't', 'o'])

Opérations ensemblistes


::

    #Pas d'opération "+" :
    >>> h = e + f
    Traceback (most recent call last):
    TypeError: unsupported operand type(s) for +: 'Set' and 'Set'
    #La réunion :
    
    >>> g = e or f
    >>> g
    Set(['t', 'o'])
    autre notation :
    >>> h | e
    Set(['a', 'b', 't', 'o'])
    >>> h & e
    Set(['t', 'o'])
    >>>
    Le complémentaire :
    >>> h = e ^ f
    >>> h
    Set(['a', 'b', 't', 'o'])
    La différence :
    >>> i = h - e
    >>> i
    L'intersection, la réunion, le complémentaire :
    >>> f
    Set(['a', 2, 3, 4, 1])
    >>> f & e
    Set(['a', 1, 2, 3, 4])
    >>> f | e
    Set(['a', 1, 2, 3, 4, 0])
    >>> f ^ e
    Set([0])

Manipulations diverses

::

    >>> ''.join( sets.Set('ciao there!') & sets.Set('hello!') )
    '!heo'

    Set(['a', 0, 2, 3, 4, 1])
    >>> f = e.copy()
    >>> f
    Set(['a', 0, 2, 3, 4, 1])
    >>> f.remove(0)
    >>> f
    Set(['a', 2, 3, 4, 1])
    >>> f & e
    Set(['a', 1, 2, 3, 4])
    >>> f | e
    Set(['a', 1, 2, 3, 4, 0])
    >>> f ^ e
    Set([0])

différence entre type et isinstance
------------------------------------

`type()` ne gère pas l'héritage alors que `isinstance()` si:

>>> class MyClass(object):
  def __init__(self):
      pass
>>> a = MyClass()
>>> type(a) == MyClass
True
>>> type(a) == object
False
>>> isinstance(a, MyClass)
True
>>> isinstance(a, object)
True
>>>

Dans la **PEP 8**, Guido demande explicitement d'utiliser isinstance et non type

.. note::

      citation :

      Object type comparisons should always use isinstance() instead
      of comparing types directly.

        Yes: if isinstance(obj, int):

        No:  if type(obj) is int:


inférence de type
~~~~~~~~~~~~~~~~~~~~~~

.. todo:: coercion, typage dynamique, inférence de type

exemple::

  def addition_forte(x, y):
    return int(x) + int(y)

  def addition_faible(x, y):
    return x + y

dans `addition_faible`, il n'y a pas de casting de type, on n'essaye pas
de transformer ça en un type donné, c'est-à-dire que si `x` et `y` sont
compatible, ça marche. Le typage est fort, on demande des types

  >>> addition_faible("1", 4)
  TypeError: cannot concatenate 'str' and 'int' objects

  >>> addition_forte("1", 4)
  5

Remarquons que `addition_forte` renvoie forcément un type `int` tandis
que `addition_faible` peut renvoyer un autre type, ceci est dû au
polymorphisme paramétrique.

.. todo:: en python un type et une classe, c'est la même chose

.. todo:: "duck typing" en python

- le typage

  - typage fort

    - statique : déclaration ou bien inférence (déduction) lisible dans le source
      (exemple : java)

    - typage dynamique : une variable est typée en fonction de son contenu

  - typage faible :

    - une donnée n'aura pas spécialement de type : les nombres, les chaînes de
      caractères, les booléens, etc. seront tous des scalaires et ne seront
      différenciés que par leur valeur et par le contexte de leur utilisation