web-dev-qa-db-fra.com

Apprentissage Python de Ruby; différences et similitudes

Je sais Ruby très bien. Je crois que je devrai peut-être apprendre Python actuellement. Pour ceux qui connaissent les deux, quels concepts sont similaires entre les deux, et qu'est ce qui est différent?

Je cherche une liste similaire à une introduction que j'ai écrite pour Learning Lua for JavaScripters : des choses simples comme la signification des espaces blancs et les constructions en boucle; le nom de nil en Python, et quelles valeurs sont considérées comme "véridiques"; est-il idiomatique d'utiliser l'équivalent de map et each, ou sont marmonner quelque chose à propos de la liste des compréhensions marmonner la norme?

Si j'obtiens une bonne variété de réponses, je suis heureux de les regrouper dans un wiki communautaire. Ou bien vous pouvez tous vous battre et vous bercer pour essayer de créer la seule vraie liste complète.

Edit : Pour être clair, mon objectif est Python "propre" et idiomatique. S'il existe un Python équivalent de inject, mais personne ne l'utilise car il existe un moyen meilleur/différent pour obtenir la fonctionnalité commune d'itération d'une liste et d'accumulation d'un résultat le long de la Je veux savoir comment vous faites les choses. Je vais peut-être mettre à jour cette question avec une liste d'objectifs communs, comment vous les atteignez dans Ruby, et demander quel est l'équivalent en Python.

128
Phrogz

Voici quelques différences clés pour moi:

  1. Ruby a des blocs; Python ne fonctionne pas.

  2. Python a des fonctions; Ruby ne le fait pas. En Python, vous pouvez prendre n'importe quelle fonction ou méthode et la passer à une autre fonction. Dans Ruby, tout est une méthode et les méthodes ne peuvent pas être transmises directement. Au lieu de cela, vous doivent les envelopper dans Proc pour les passer.

  3. Ruby et Python prennent tous deux en charge les fermetures, mais de différentes manières. En Python, vous pouvez définir une fonction à l'intérieur d'une autre fonction. La fonction interne a un accès en lecture aux variables de la fonction externe, mais pas un accès en écriture Dans Ruby, vous définissez les fermetures à l'aide de blocs. Les fermetures ont un accès complet en lecture et en écriture aux variables de la portée externe.

  4. Python a des compréhensions de liste, qui sont assez expressives. Par exemple, si vous avez une liste de nombres, vous pouvez écrire

    [x*x for x in values if x > 15]
    

    pour obtenir une nouvelle liste des carrés de toutes les valeurs supérieures à 15. Dans Ruby, vous devez écrire ce qui suit:

    values.select {|v| v > 15}.map {|v| v * v}
    

    Le code Ruby n'est pas aussi compact. Il n'est pas aussi efficace car il convertit d'abord le tableau de valeurs en un tableau intermédiaire plus court contenant les valeurs supérieures à 15. Ensuite, il prend le tableau intermédiaire et génère un tableau final contenant les carrés des intermédiaires. Le tableau intermédiaire est ensuite jeté. Donc, Ruby se termine avec 3 tableaux en mémoire pendant le calcul; Python n'a besoin que de la liste d'entrée et de la liste résultante.

    Python fournit également des compréhensions de cartes similaires.

  5. Python prend en charge les tuples; Ruby non. Dans Ruby, vous devez utiliser des tableaux pour simuler des tuples.

  6. Ruby prend en charge les instructions switch/case; Python ne fonctionne pas.

  7. Ruby prend en charge la norme expr ? val1 : val2 opérateur ternaire; Python ne fonctionne pas.

  8. Ruby ne prend en charge que l'héritage unique. Si vous devez imiter l'héritage multiple, vous pouvez définir des modules et utiliser des mix-ins pour extraire les méthodes de module en classes. Python prend en charge l'héritage multiple plutôt que les mélanges de modules.

  9. Python ne prend en charge que les fonctions lambda sur une seule ligne. Ruby blocs, qui sont des sortes/fonctions de fonctions lambda, peuvent être arbitrairement grands. Pour cette raison, Ruby est généralement écrit dans un style plus fonctionnel) que Python code. Par exemple, pour parcourir une liste dans Ruby, vous faites généralement

    collection.each do |value|
      ...
    end
    

    Le bloc fonctionne comme une fonction passée à collection.each. Si vous deviez faire la même chose en Python, vous devez définir une fonction interne nommée, puis la transmettre à la collection de chaque méthode (si la liste prend en charge cette méthode):

    def some_operation(value):
      ...
    
    collection.each(some_operation)
    

    Cela ne coule pas très bien. Ainsi, l'approche non fonctionnelle suivante serait généralement utilisée en Python:

    for value in collection:
      ...
    
  10. L'utilisation des ressources de manière sûre est très différente entre les deux langues. Ici, le problème est que vous souhaitez allouer une ressource (ouvrir un fichier, obtenir un curseur de base de données, etc.), effectuer une opération arbitraire sur celui-ci, puis le fermer de manière sûre même si une exception se produit.

    Dans Ruby, parce que les blocs sont si faciles à utiliser (voir # 9), vous coderiez généralement ce modèle comme une méthode qui prend un bloc pour que l'opération arbitraire s'exécute sur la ressource.

    En Python, passer une fonction pour l'action arbitraire est un peu plus maladroit puisque vous devez écrire une fonction interne nommée (voir # 9). Au lieu de cela, Python utilise une instruction with pour une gestion sûre des ressources. Voir Comment nettoyer correctement un objet Python? pour plus de détails.

151
Clint Miller

Je viens de passer quelques mois à apprendre Python après 6 ans de Ruby. Il n'y avait vraiment pas de grande comparaison pour les deux langues, alors j'ai décidé de m'écrire et d'en écrire une moi-même. Maintenant, c'est c'est principalement concerné par la programmation fonctionnelle, mais comme vous mentionnez la méthode inject de Ruby, je suppose que nous sommes sur la même longueur d'onde.

J'espère que cela aide: La 'laideur' de Python

Quelques points qui vous feront avancer dans la bonne direction:

  • Toutes les qualités de programmation fonctionnelle que vous utilisez dans Ruby est en Python, et c'est encore plus facile. Par exemple, vous pouvez mapper les fonctions exactement comme vous vous y attendez:

    def f(x):
        return x + 1
    
    map(f, [1, 2, 3]) # => [2, 3, 4]
    
  • Python n'a pas de méthode qui agit comme each. Puisque vous n'utilisez que each pour les effets secondaires, l'équivalent dans Python est la boucle for:

    for n in [1, 2, 3]:
        print n
    
  • Les compréhensions de listes sont excellentes lorsque a) vous devez traiter ensemble des fonctions et des collections d'objets et b) lorsque vous devez itérer à l'aide de plusieurs index. Par exemple, pour trouver tous les palindromes dans une chaîne (en supposant que vous ayez une fonction p() qui renvoie vrai pour les palindromes), tout ce dont vous avez besoin est une compréhension de liste unique:

    s = 'string-with-palindromes-like-abbalabba'
    l = len(s)
    [s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]
    
27
David J.

Ma suggestion: n'essayez pas d'apprendre les différences. Apprenez à aborder le problème en Python. Tout comme il existe une approche Ruby pour chaque problème (qui fonctionne très bien avec les limites et les points forts du langage), il existe une approche Python pour le problème). ils sont tous les deux différents. Pour tirer le meilleur parti de chaque langue, vous devez vraiment apprendre la langue elle-même, et pas seulement la "traduction" de l'une à l'autre.

Maintenant, cela dit, la différence vous aidera à vous adapter plus rapidement et à apporter 1 modification à un programme Python. Et c'est bien pour commencer à écrire. Mais essayez d'apprendre des autres projets les pourquoi derrière les décisions d'architecture et de design plutôt que comment derrière la sémantique du langage ...

10
ircmaxell

Je connais le petit Ruby, mais voici quelques points sur les choses que vous avez mentionnées:

  • nil, la valeur indiquant l'absence de valeur, serait None (notez que vous la vérifiez comme x is None ou x is not None, pas avec == - ou par coercition à booléen, voir point suivant).
  • None, nombres zéro-esque (0, 0.0, 0j (nombre complexe)) et collections vides ([], {}, set(), la chaîne vide "", Etc.) sont considérées comme fausses, tout le reste est considéré comme véridique.
  • Pour les effets secondaires, (for-) boucle explicitement. Pour générer un nouveau tas de choses sans effets secondaires, utilisez des compréhensions de liste (ou leurs proches - expressions de générateur pour les itérateurs ponctuels paresseux, compréhensions dict/set pour lesdites collections).

Concernant le bouclage: vous avez for, qui fonctionne sur un itérable (! Pas de comptage), et while, qui fait ce que vous attendez. Le fromer est beaucoup plus puissant, grâce à la prise en charge étendue des itérateurs. Non seulement presque tout ce qui peut être un itérateur au lieu d'une liste est un itérateur (au moins dans Python 3 - in Python 2, vous avez les deux et le par défaut est une liste, malheureusement.) Il existe de nombreux outils pour travailler avec les itérateurs - Zip itère un nombre illimité d'itérables en parallèle, enumerate vous donne (index, item) (sur any itérable, pas seulement sur les listes), même en tranchant les itérables abritaires (peut-être grandes ou infinies)! J'ai trouvé que cela rend beaucoup de tâches de bouclage beaucoup plus simples. , etc.

8
user395760

Dans Ruby, les variables et méthodes d'instance sont complètement indépendantes, sauf lorsque vous les associez explicitement avec attr_accessor ou quelque chose comme ça.

En Python, les méthodes ne sont qu'une classe d'attribut spéciale: celle qui est exécutable.

Ainsi, par exemple:

>>> class foo:
...     x = 5
...     def y(): pass
... 
>>> f = foo()
>>> type(f.x)
<type 'int'>
>>> type(f.y)
<type 'instancemethod'>

Cette différence a beaucoup d'implications, comme par exemple que faire référence à f.x fait référence à l'objet méthode, plutôt que de l'appeler. De plus, comme vous pouvez le voir, f.x est public par défaut, alors que dans Ruby, les variables d'instance sont privées par défaut.

6
Paul Prescod