php var_dump VS python repr !

Un exemple en Php :

<?php

$a = 'bar "test" bar';
var_dump($a);
// Sortie : string(14) "bar "test" bar"

$b = 'bar \'test\' bar';
var_dump($b);
// Sortie : string(14) "bar 'test' bar"

$c = 'bar \'test\' "test" bar';
var_dump($c);
// Sortie : string(21) "bar 'test' "test" bar"

$d = array(
    'foo' => 'bar \'test\' "test" bar'
);
var_dump($d);
/* Sortie :
    array(1) {
      ["foo"]=>
      string(21) "bar 'test' "test" bar"
    }
*/

?>

la sortie de ce script :

string(14) "bar "test" bar"
string(14) "bar 'test' bar"
string(21) "bar 'test' "test" bar"
array(1) {
  ["foo"]=>
  string(21) "bar 'test' "test" bar"
}

Même chose en Python :

>>> a = 'bar "test" bar'
>>> print(repr(a))
'bar "test" bar'

>>> b = 'bar \'test\' bar'
>>> print(repr(b))
"bar 'test' bar"

>>> c = 'bar \'test\' "test" bar'
>>> print(repr(c))
'bar \'test\' "test" bar'

>>> d = { 'foo': 'bar \'test\' "test" bar' }
>>> print(d)
{'foo': 'bar \'test\' "test" bar'}

Observatons :

  • PHP oublie d'afficher les caractères d'échappements
  • Python affiche correctement les caractères d'échappements
  • Python prend soin d'afficher au mieux les guillemets : soit utilise des simples "quotes" ou des "double quotes"
Read and Post Comments

Dans la série « Ce qui me fait préférer Python » : « arguments optionnels et nommés »

Une des fonctionnalités qui me fait préférer Python à d'autre langage est la possibilité d'utiliser des arguments nommés dans les fonctions ou les méthodes.

Exemple en PHP

Je commence par écrire une fonction avec des paramètres optionnels :

<?php

function affiche_contact($nom, $prenom, $telephone1 = "", $telephone2 = "", $email = "")
{
    print("Nom : $nom\n");
    print("Prénom : $prenom\n");
    print("Téléphone1 : $telephone1\n");
    print("Téléphone2 : $telephone2\n");
    print("Email : $email);
}

?>

J'affiche un contact sans spécifier aucun argument optionnel :

<?php
affiche_contact("Dupond", "Pierre");
?>

Jusque là tout va bien. A noter tout de même que sans lire l'entête de la fonction, il n'est pas possible de savoir à quoi correspond chaque argument. De plus il n'est pas possible d'envoyer en premier argument le prénom puis le nom. L'entête de la fonction déclare que c'est d'abord le nom, puis le prénom, il n'est pas possible de changer cet ordre lors de l'appel de la fonction. Vous comprendrez ensuite pourquoi j'ai donné cette précision.

J'affiche un contact mais cette fois je spécifie l'argument optionnel $telephone1 :

<?php
affiche_contact("Dupond", "Pierre", "03 87 20 25 20");
?>

Toujours aucun problème, l'argument $telephone1 est le premier argument optionnel.

Je souhaite maintenant afficher un contact avec un numéro de téléphone et une adresse mail :

<?php
affiche_contact("Dupond", "Pierre", "03 87 20 25 20", "", "dupond.pierre@example.com");
?>

Comme vous pouvez le voir, si je souhaite passer le paramètre $email je suis obligé d'envoyer l'argument $telephone2. Ceci n'est vraiment pas pratique ni élégant. De plus, je dois avoir connaissance de la valeur par défaut du paramètre que je ne souhaitais pas envoyer.

Ce cas de figure est une vrai limite au arguments optionnels des fonctions du langage PHP. J'ai souvent été confronté à ce problème qui est vraiment énervant lorsque l'on souhaite étendre les fonctionnalités d'une fonction sans casser une API existante.

Exemple en Python

Je commence par écrire une fonction avec des paramètres optionnels :

def affiche_contact(nom, prenom, telephone1 = "", telephone2 = "", email = ""):
    print ("""Nom : %s
        Prénom : %s
        Téléphone1 : %s
        Téléphone2 : %s
        Email : %s""" % (nom, prenom, telephone1, telephone2, email))

J'affiche un contact sans spécifier aucun arguement optionnel tout comme avec la syntax PHP :

affiche_contact("Dupond", "Pierre")

Je peux aussi utiliser la syntaxe suivante :

affiche_contact(nom = "Dupond", prenom = "Pierre")

Cette syntaxe nous indique la correspondance de chaque champ envoyé. Cela évite au lecteur de consulter l'entête de la fonction.

Je peux aussi envoyer les aguments dans un ordre quelconque :

affiche_contact(prenom = "Pierre", nom = "Dupond")

J'affiche un contact mais cette fois je spécifie l'arguement optionnel telephone1 :

affiche_contact("Dupond", "Pierre", "03 87 20 25 20")

L'argument telephone1 est le premier arguement optionnel, il n'est donc pas obligatoire de le nommer.

Je souhaite maintenant afficher un contact avec un numéro de téléphone et une adresse mail :

affiche_contact("Dupond", "Pierre", "03 87 20 25 20", email = "dupond.pierre@example.com")

Comme vous pouvez le constater, j'ai envoyé uniquement les paramètres optionnels que je souhaite transmettre, contrairement à PHP je n'ai pas besoin de transmettre le paramètre telephone2.

Les arguments optionnels nommés sont vraiment une fonctionnalité Python très pratique. Cela permet de vraiment choisir les paramètres que l'on souhaite envoyer sans se soucier de l'ordre de ces derniers.

Les arguments nommés ont aussi l'avantage d'indiquer au lecteur le nom de l'argument envoyé sans avoir à regarder l'entête de la fonction.

Solution de contournement en PHP

En PHP, il y a aussi une méthode pour produire plus ou moins le même effet.

Exemple :

<?php

function affiche_contact($data)
{
    if (!isset($data['telephone1'])) $data['telephone1'] = '';
    if (!isset($data['telephone2'])) $data['telephone2'] = '';
    if (!isset($data['email'])) $data['email'] = '';

    print("Nom : " . $data['nom'] . "\n");
    print("Prénom : " . $data['prenom'] . "\n");
    print("Téléphone1 :" . $data['telephone1'] . "\n");
    print("Téléphone2 :" . $data['telephone2'] . "\n");
    print("Email :" . $data['email']);
}

?>

Puis lors de l'appel de la fonction :

<?php

affiche_contact(array("nom": "Dupond", "prenom": "Pierre"));
affiche_contact(array("nom": "Dupond", "prenom": "Pierre", "telephone1": "03 87 20 25 20"));
affiche_contact(array(
    "nom" => "Dupond",
    "prenom" => "Pierre",
    "telephone1" => "03 87 20 25 20",
    "email" => "dupond.pierre@example.com"
));

?>

Ceci est plus long (surtout dans le corps de la fonction) et moins élégant qu'en Python où cette fonctionnalité est directement intégrée au langage.

Conclusion

Je trouve que cette fonctionnalité du langage Python est très pratique bien qu'elle ne soit pas une killer feature du langage.

Références

Pour finir, quelques références :

Read and Post Comments

XML-RPC Python vs PHP

Il y a quelques mois, j'ai eu à développer un serveur XML-RPC en PHP. Il m'est alors venu à l'esprit que l'utilisation de ce protocole était un bon exemple de comparaison entre PHP et Python (avec préalablement une petite idée derrière la tête).

Le but de ce document est donc de comparer l'utilisation d'une l'API XML-RPC en langage PHP et en langage python.

1   Avant propos

Par honnêteté, je tiens à préciser qu'il m'a été difficile d'écrire une comparaison objective alors que j'avais déjà une conclusion à l'esprit.
Pour luter contre ce manque d'objectivité, j'ai approfondi mes connaissances au niveau de la solution la moins efficace. Cela n'a pas été inutile car j'ai justement trouvé une solution qui simplifie le code dans le langage le moins mis en valeur.
Par conséquent, je pense que ce document est assez équilibré et pas trop partisan... enfin... je l'espère...

2   En PHP

2.1   Choix de la librairie

Avec PHP, le problème se pose déjà au niveau du choix de la librairie... et on peut dire qu'on a le choix, voici celles que j'ai trouvé (via XMLRPC.com [1] et Google) :

  • XMLRPC-EPI - binding PHP 4.1 [2]
  • XML-RPC Library for PHP [3]
  • phpRPC [4]
  • The Incutio XML-RPC Library for PHP [5]
  • SimpleXMLRPC for PHP4 [6]
  • XML-RPC Functions - intégré à PHP mais en expérimental basé sur XMLRPC-EPI [7]
  • XML-RPC for PHP [8]
  • Pear XML_RPC Package [9]

Liste sans doute incomplète car je n'ai pas beaucoup cherché...

Ca fait du monde tout ça ! En théorie pour faire la même chose !

Normalement, les deux solutions les plus "officiels" sont :

  • XML-RPC Functions - intégré à PHP mais en expérimental - basé sur XMLRPC-EPI [10]
  • Pear XML_RPC Package [11]

Je ne sais pas pourquoi la librairie XML-RPC interne à PHP est en état expérimental depuis décembre 2001 dans PHP 4.1 ! Cela ne donne pas vraiment envie de l'utiliser même si je pense qu'elle doit bien fonctionner. Si je me réfère aux discussions sur la mailing-list des développeurs PHP [12], la librairie XML-RPC interne de PHP est voué à disparaître au profit de la librairie Pear.

Dans mes précédents développements, j'ai utilisé la libraire Pear XML-RPC, c'est donc celle-ci que je vais utiliser dans l'exemple PHP de ce document.
Je ne sais pas si c'est le meilleur choix... mais en tout les cas, cela me parait la solution la plus officielle.
[1]http://www.xmlrpc.com/directory/1568/implementations
[2]http://xmlrpc-epi.sourceforge.net/
[3]http://keithdevens.com/software/xmlrpc
[4]http://sourceforge.net/projects/phprpc/
[5]http://scripts.incutio.com/xmlrpc/
[6]http://simplexmlrpc.sourceforge.net/
[7]http://fr.php.net/manual/en/ref.xmlrpc.php
[8]http://sourceforge.net/projects/phpxmlrpc/
[9]http://pear.php.net/package/XML_RPC/redirected
[10]http://fr.php.net/manual/en/ref.xmlrpc.php
[11]http://pear.php.net/package/XML_RPC/redirected
[12]http://marc.info/?l=php-internals&m=112519107120577&w=2

2.2   Installation de Pear XML-RPC

Je pars du principe que Pear [13] est installé sur votre système. Pour installer le paquet "XML_RPC" vous devez entrer les commandes suivantes :

$ sudo pear channel-update "pear.php.net"
$ sudo pear install XML_RPC

Normalement, si tout c'est bien passé, le paquet devrait être installé.

[13]http://fr.wikipedia.org/wiki/PEAR

2.3   La documentation de Pear XML-RPC

Alors ne faite pas comme moi, ne passez pas à coté de la documentation utilisateur qui se trouve derrière "End-user Documentation" à gauche de la page documentation.
Avant la rédaction de ce document, je m'étais focalisé sur la "documentation de l'API" à droite de la "page documentation". Malheureusement, cette documentation est très pauvre. Vous trouverez bien plus d'information dans la partie "End-user Documentation".

3   Exemple 1 en PHP

Cet exemple met en oeuvre la technique que j'ai utilisé lors de mes précédents développements. Vous verrez ensuite une solution plus simple dans le second exemple.

3.1   Exemple : partie serveur

<?php
require_once('XML/RPC/Server.php');

function somme($params)
{
    $x = $params->getParam(0)->getval();
    $y = $params->getParam(1)->getval();

    return new XML_RPC_Response(new XML_RPC_Value($x * $y, 'int'));
}

function renvoi_tableau_fois_2($params)
{
    $tableau = $params->getParam(0);
    $result = array();
    for($i = 0; $i < $tableau->arraysize(); $i++) {
        $result[] = new XML_RPC_Value($tableau->arraymem($i)->getval() * 2, 'int');
    }

    return new XML_RPC_Response(new XML_RPC_Value($result, 'array'));
}

function concat_dict($params)
{
    $result = array();
    $dict1 = $params->getParam(0);
    while($element = $dict1->structeach()) {
        $result[$element['key']] = new XML_RPC_Value($element['value']->getval(), 'string');
    }

    $dict2 = $params->getParam(1);
    while($element = $dict2->structeach()) {
        $result[$element['key']] = new XML_RPC_Value($element['value']->getval(), 'string');
    }

    return new XML_RPC_Response(new XML_RPC_Value($result, 'struct'));
}

$server = new XML_RPC_Server(
     array(
         'somme' => array('function' => 'somme'),
         'renvoi_tableau_fois_2' => array('function' => 'renvoi_tableau_fois_2'),
         'concat_dict' => array('function' => 'concat_dict')
    ),
    1 // serviceNow
);

?>

3.2   Exemple : partie client

<?php
require_once('XML/RPC.php');

$client = new XML_RPC_Client('/xmlrpc/server2.php', 'localhost');
$client->setDebug(1);
$response = $client->send(
    new XML_RPC_Message('somme', array(
        new XML_RPC_Value(4, 'int'),
        new XML_RPC_Value(8, 'int')
    ))
);
print $response->value()->getval();
// Affiche : 12

$response = $client->send(
    new XML_RPC_Message('renvoi_tableau_fois_2', array(
        new XML_RPC_Value(array(
            new XML_RPC_Value(1, 'int'),
            new XML_RPC_Value(2, 'int'),
            new XML_RPC_Value(3, 'int'),
            new XML_RPC_Value(4, 'int')
        ), 'array')
    ))
);
print("<pre>");
for($i = 0; $i < $response->value()->arraysize(); $i++) {
    print $response->value()->arraymem($i)->getval() . "\n";
}
print("</pre>");

/* Affiche :
2
4
6
8
*/

$response = $client->send(
    new XML_RPC_Message('concat_dict', array(
        new XML_RPC_Value(
            array("key1" => new XML_RPC_Value("value1", "string")),
            'struct'
        ),
        new XML_RPC_Value(
            array("key2" => new XML_RPC_Value("value2", "string")),
            'struct'
        )
    ))
);

print("<pre>");
while($element = $response->value()->structeach()) {
    print($element['key'] . '=>'. $element['value']->getval() . "\n");
}
print("</pre>");
/* Affiche :
key1=>value1
key2=>value2
*/
?>

4   Exemple 2 en PHP

En réalisant ce document, j'ai découvert les méthodes XML_RPC_Encode et XML_RPC_Decode. Ces fonctions diminuent fortement la quantité de code nécessaire pour "coder" et "décoder" les entrées/sorties des fonctions XML-RPC.

Voici le même exemple que le précédent mais en une version réduite grâce à l'utilisation de XML_RPC_Encode et XML_RPC_Decode.

4.1   Exemple : partie serveur

<?php
require_once('XML/RPC/Server.php');

function somme($params)
{
    $x = XML_RPC_Decode($params->getParam(0));
    $y = XML_RPC_Decode($params->getParam(1));

    return new XML_RPC_Response(XML_RPC_Encode($x * $y));
}

function renvoi_tableau_fois_2($params)
{
    $tableau = XML_RPC_Decode($params->getParam(0));
    $result = array();
    for($i = 0; $i < count($tableau); $i++) {
        $result[] = $tableau[$i] * 2;
    }

    return new XML_RPC_Response(XML_RPC_Encode($result, 'array'));
}

function concat_dict($params)
{
    $result = array();
    $dict1 = XML_RPC_Decode($params->getParam(0));
    foreach($dict1 as $key => $value) {
        $result[$key] = $value;
    }

    $dict2 = XML_RPC_Decode($params->getParam(1));
    foreach($dict2 as $key => $value) {
        $result[$key] = $value;
    }

    return new XML_RPC_Response(XML_RPC_Encode($result));
}

$server = new XML_RPC_Server(
     array(
         'somme' => array('function' => 'somme'),
         'renvoi_tableau_fois_2' => array('function' => 'renvoi_tableau_fois_2'),
         'concat_dict' => array('function' => 'concat_dict')
    ),
    1 // serviceNow
);

?>

4.2   Exemple : partie client

<?php
require_once('XML/RPC.php');

$client = new XML_RPC_Client('/xmlrpc/server3.php', 'localhost');
$client->setDebug(1);
$response = $client->send(
    new XML_RPC_Message('somme', array(
        XML_RPC_Encode(4),
        XML_RPC_Encode(8)
    ))
);
print XML_RPC_Decode($response->value());
// Affiche : 12

$response = XML_RPC_Decode($client->send(
    new XML_RPC_Message('renvoi_tableau_fois_2', array(XML_RPC_Encode(
        array(1, 2, 3, 4)
    )))
)->value());
print("<pre>");
for($i = 0; $i < count($response); $i++) {
    print $response[$i] . "\n";
}
print("</pre>");

/* Affiche :
2
4
6
8
*/

$response = XML_RPC_Decode($client->send(
    new XML_RPC_Message('concat_dict', array(
        XML_RPC_Encode(array("key1" => "value1")),
        XML_RPC_Encode(array("key2" => "value2")),
    ))
)->value());

print("<pre>");
foreach($response as $key => $value) {
    print($key . '=>'. $value . "\n");
}
print("</pre>");
/* Affiche :
key1=>value1
key2=>value2
*/
?>

Cette version est tout de même beaucoup plus simple que le précédent mais nous allons voir que la version Python est encore plus simple !

5   En Python

Avec Python, le choix de la librairie XML-RPC est très simple, il suffit d'utiliser les classes disponibles dans la librairie standard de Python [14]. Encore une fois, la communauté Python évite la multiplication des projets similaires. La communauté essaie si c'est possible de n'avoir qu'une librairie et de l'améliorer constamment.

[14]Internet Protocols and Support - http://docs.python.org/lib/internet.html

5.1   Documentation

Documentation concernant la partie XML-RPC de la librairie standard Python :

  • Section "XML-RPC client access" [15] qui contient entre autre la documentation de la classe xmlrpclib, un exemple de client [16]...
  • Section "Basic XML-RPC server" [17] qui contient entre autre la documentation de la classe SimpleXMLRPCServer, un exemple de serveur...
[15]http://docs.python.org/lib/module-xmlrpclib.html
[16]http://docs.python.org/lib/xmlrpc-client-example.html
[17]http://docs.python.org/lib/module-SimpleXMLRPCServer.html

5.2   Exemple : partie serveur

Cet exemple est identique à la partie serveur des précédents exemples écris en PHP.

from SimpleXMLRPCServer import SimpleXMLRPCServer

# Create server
server = SimpleXMLRPCServer(("localhost", 8080))
server.register_introspection_functions()

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

server.register_function(somme)

def renvoi_tableau_fois_2(tableau):
    return [v * 2 for v in tableau]

server.register_function(renvoi_tableau_fois_2)

def concat_dict(dict1, dict2):
    dict1.update(dict2)
    return dict1

server.register_function(concat_dict)

server.serve_forever()
On constate que l'écriture de fonctions XML-RPC est très simple. La méthode est scrupuleusement identique à l'écriture de fonctions python tout à fait standard (ligne 7 et 8, 12 et 13, 17 à 19). Le passage de paramètres est réalisé d'une manière totalement transparente.
La seule chose à faire pour rendre une fonction disponible via XML-RPC est de "l'inscrire" au serveur via la fonction "register_function" (lignes 10, 15 et 21).

5.3   Exemple : partie client

Cet exemple est identique à la partie client des précédents exemples écris en PHP.

import xmlrpclib

s = xmlrpclib.Server("http://localhost:8080")
print s.somme(4, 8)
# Affiche : 12

print s.renvoi_tableau_fois_2([1, 2, 3, 4])
# Affiche [2, 4, 6, 8]

print s.concat_dict({"key1": "value1"}, {"key2": "value2"})
# Affiche {'key2': 'value2', 'key1': 'value1'}

Pour la partie client, c'est encore plus simple car il suffit de créer un objet "xmlrpclib.Server" et via cet objet, appeler les fonctions XML-RPC d'une manière totalement transparente... comme si ces fonctions étaient des méthodes natives de la classe "xmlrpclib.Server".

6   Bilan

Voici un petit bilan de cette comparaison sous la forme d'un tableau :

  Python PHP
Choix de la librairie simple : une solution officiel documenté et maintenu
difficile :
  • nombreuses librairies
  • difficulté à trouver la solution la plus officiel
Solution retenu Librairie standard Python Librairie Pear XML-RPC
Entrées / Sortie des fonctions XML-RPC Aucun traitement spécifique ! Les fonctions sont écrites comme des fonctions Python standards Il faut coder et décoder toutes les entrées sorties ! => lourdeur du code, difficulté de mise au point et de débogage
Déclaration des fonctions XML-RPC Simple passage de la fonction en paramètre de la méthode register_function Gros tableau imbriqué à passer en paramètre du constructeur XML_RPC_Server
Appel des fonctions Très simple Très lourd
Avantages
  • quantité de code minimal
  • transparence de la solution : très peu de différence entre l'utilisation d'une fonction distante et local.
  • choix de la librairie à utiliser
  • documentation complète
?
Inconvénients ?
  • difficulté de choix de la librairie
  • documentation pas très soignée
  • aucune transparence : l'utilisation, la création ou l'utilisation d'une fonction XML-RPC est totalement différente à la création et à l'utilisation de fonctions PHP standards
  • grande quantité de code nécessaire

Autre chose : j'ai plus d'expérience en PHP qu'en Python mais la solution Python est celle qui a été pour moi la plus évidente et qui m'a demandé le moins de recherche.

7   Conclusion

L'API XML-RPC de Python est un bon exemple pour illustrer une des forces de Python : rendre les choses simples et concises.

Vous pourrez faire le même constat dans d'autres librairies Python traitant du "même" domaine d'activité :

J'essaierais peut être dans un prochain article d'expliquer pourquoi Python permet de réaliser des API très simples et concises...

Read and Post Comments

Mes flux :