J'ai une situation où je voudrais trancher conditionnellement une chaîne à partir de la position signalée d'un symbole «@»; la condition étant: couper la chaîne si le '@' est là, sinon la laisser intacte. J'ai pensé de deux façons, l'une en utilisant une fonction, l'autre en utilisant une expression conditionnelle en ligne. Quelle méthode est la plus pythonique?

Utiliser une fonction

>>> def slice_from_at(inp):
...     res = inp.find('@')
...     if res == -1:
...         return None
...     else:
...         return res     
>>> c = 'agent_address@agent_address'
>>> c[:slice_from_at(c)]
... 'agent_address'

Utilisation d'une expression conditionnelle en ligne

>>> c = 'agent_address@agent_address'
>>> c[:None if c.find('@') == -1 else c.find('@')]
... 'agent_address'

Bien que l'utilisation de l'expression conditionnelle en ligne soit plus concise et, certains peuvent argumenter plus économique - la méthode de la fonction est-elle plus Pythonic parce qu'elle est plus lisible?

3
Raz 7 nov. 2011 à 13:42

6 réponses

Meilleure réponse

Non seulement une fonction est plus lisible, mais elle est également réutilisable.

L'expression en ligne peut appeler c.find('@') deux fois, ce qui est inefficace.

Comme Useless l'a mentionné dans les commentaires, il existe déjà des fonctions intégrées pour ce faire; vous n'avez vraiment pas besoin de définir votre propre fonction dans ce cas:

agent,_,address = c.partition('@')

Soit dit en passant, un rappel est une fonction qui est passée en argument et appelée ultérieurement. Vous n'avez pas de rappel car il n'est pas appelé plus tard. Je pense que cela devrait simplement être appelé une fonction.

5
unutbu 7 nov. 2011 à 10:10

Et à la place?

c.split('@')[0]
0
Marcelo Cantos 7 nov. 2011 à 09:46

La raison stylistique selon laquelle [existant, mais vous auriez pu les écrire vous-même] des fonctions comme .partition() ou .rsplit() sont plus propres que c[:slice_from_at(c)] que l'API de slice_from_at() est de bas niveau: il renvoie un index dans la chaîne qui lui a été donnée, mais cet index n'a de sens que pour couper la chaîne, alors pourquoi ne pas retourner directement le ou les fragments souhaités de la chaîne?

Généralement, les index en séquences sont considérés comme non pythoniques lorsqu'il existe une alternative de niveau supérieur, comme en témoigne la quantité croissante d'aides intégrées qui réduisent l'exposition aux index. Exemples particuliers qui me viennent à l'esprit:

  • zip() et enumerate() réduisant le besoin de for i in range(len(seq)): boucles.
  • substring in string, str.split(), puis str.partition().
0
Beni Cherniavsky-Paskin 7 nov. 2011 à 11:15

Le plus pythonique?

Ne réinventez pas la roue, utilisez str.partition()

def slice_from_at(inp):
    if '@' in inp:
        return inp.partition('@')[2]
    return inp

Si vous êtes plus préoccupé par la vitesse que par la lisibilité, essayez str.rsplit():

def slice_from_at(inp):
    return inp.rsplit('@', 1)[-1]

Aucun de vos exemples ne comprend de "rappel". Ils ne devraient pas non plus.

Une fonction bien nommée qui fait une chose et qui fait bien une chose est à peu près aussi Pythonique que possible. S'il est sauvegardé avec des tests unitaires, tant mieux.

3
Johnsyweb 7 nov. 2011 à 10:08

J'ai tendance à préférer les méthodes de chaîne quand elles font le travail facilement, mais pour être complet, les expressions régulières doivent être mentionnées:

re.sub('^.*@', '', c)
0
Beni Cherniavsky-Paskin 7 nov. 2011 à 11:18

Je code comme ceci:

if '@' in c:
    c = c[:c.find('@')]

Je ne déplace pas 2 lignes de code pour séparer la fonction dans la plupart des cas.

0
Ski 7 nov. 2011 à 11:32