IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Comment bien programmer, 2 classes totalement identiques avec contenu des méthodes différentes ?


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2011
    Messages
    49
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 49
    Par défaut Comment bien programmer, 2 classes totalement identiques avec contenu des méthodes différentes ?
    Bonjour, je reprends la programmation pour un petit robot de trading.
    J'ai eu l'idée de créer une classe : Exchange

    Je l'instancie ainsi : coinbase = Exchange(self, pair, o)
    Dans cet exemple, pair correspond à la pair où on récupère les données de la classe (dans une première version cela ne gère qu'une seule paire).
    o correspond à un objet déjà instancié qui contient des méthodes pour faire des requêtes API (et la connexion avec Secret_key..)

    Le problème est le suivant :

    Je vais ensuite créer par exemple : binance = ExchangeOther(self, market, o)

    Dans ce cas de figure, o sera un objet complètement différent, avec des méthodes différentes (le contenu des méthodes serait différents, mais le nom serait le même)


    Cependant, à la fin, l'objet binance et l'objet coinbase seront donné à un objet Robot.

    Ces 2 objets seront des noms de classes différentes, mais en même temps, ils auront exactement les mêmes attributs et méthodes. La seule différence sera le contenu des méthodes, car leur objet o (qui contient les requète API) ne possède par les mêmes méthodes).

    Cependant, pour le futur, j'ai le sentiment que plus mes objets Exchanges sont proches, et plus ce sera facile de les donner au robot pour travailler.

    Devrais-je m'orienter vers une spécificité des classes (héritage?polymorphisme?classe abstraite?)

    Pouvez vous m'aiguiller. Merci.

  2. #2
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 835
    Billets dans le blog
    1
    Par défaut
    Bonjour

    Ton souci "semble" (je mets entre guillemets car je fais là une hypothèse gratuite) être le résultat d'une mauvaise analyse du besoin initial. J'espère me tromper et j'espère que tu ne m'en voudras pas.
    Tu dis que tu as deux objets différents mais même attributs et méthodes. Sauf que le second "semble" (ben oui, sans vrai code pour regarder je ne peux que faire des conjectures) être un enrichissement du premier.
    Si c'est vrai, alors il faut passer par l'héritage qui est justement fait pour ça. Ainsi le second n'aura à réécrire que les méthodes qui diffèrent. Et en plus tu pourras ne donner au robot que le dernier qui, étant un enrichissement du premier, aura donc accès de-facto aussi au premier.
    Ne reste que ce "o" que je n'ai pas bien pigé. D'après ta description, il "semble" () être un objet (et non pas une instance d'objet). Mais si c'est le cas, tu ne peux pas avoir deux objets nommés "o" et être comme tu le dis différents d'un cas sur l'autre...

    En tout cas quand on dit "j'ai deux trucs de même noms mais qui ne sont pas les mêmes", généralement c'est que ça sent pas bon...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2011
    Messages
    49
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 49
    Par défaut
    Merci pour la réponse. Object, instance d'objet, je ne cerne pas la différence.

    J'ai un :

    from API_exchange_1 import Api1
    from API_exchange_2 import Api2

    Puis :
    o1 = api1.v1
    o2 = api2.constructeur()

    exhange1 = Exchange1(o1, "BTC-USDT")
    exhange2 = Exchange2(o1, "BTC-USDT")



    exchange1 et exchange2 sont des classes à presque entièrement similaires.

    Voici une méthode qu'ils ont comme différence, les méthodes ont les mêmes (dans cet exemple, self.API est l'objet o donné au constructeur)
    En gros, cette méthode retourne une valeur qu'elle va chercher dans un Json retournée par la requête API.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	def get_best_book_bid(self):
    		return float(self.API.get_symbol_book(self.pair,4,1)[0]["data"]["buys"][0]["price"])
     
    	def get_best_book_bid(self):
    		return float(self.API.get_orderbook(self.pair)["bid"][0]["rate"])

    Comme les API pour accéder aux données des exchanges sont différentes, c'est la raison pour laquelle j'ai une classe par exchange.

    Je pense qu'au final je créerai une classe Robot, et je créerai un objet Robot contenant mes exchanges. Avec une sorte de bouble infinie robot.run() dans laquelle le robot lancera des opérations et vérifications.

    J'ai des connaissances assez basiques, je sais faire des algo, mais concernant la structure des langages et les implémentations les plus convenables, je suis à un niveau de deuxième année universitaire d'il y a 10 ans... Mais je souhaite progresser si vous avez des conseils pour l'implémentation.

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 835
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par passio Voir le message
    Object, instance d'objet, je ne cerne pas la différence.
    Ah ok. En fait c'est pas compliqué: une instance (le terme exact est "instance de classe") c'est simplement une variable du type de la classe. Tu as d'un côté la classe (le code avec "class ...") et ensuite quand tu crées des variables de cette classe, tu crées des "instances de classe".
    Si tu écris a=5, "5" étant un int, et un int étant un objet, "a" est alors une instance d'int ou (plus simplement pour le cas présent) un int.
    Quand tu écris exhange1 = Exchange1(o1, "BTC-USDT"), exhange1 est une instance de Exchange1. Accessoirement là peut-être tu aurais avantage à distinguer plus explicitement les noms de tes classes pour éviter les confusions qui peuvent suivre. Par exemple moi je nomme toutes mes classes "cXXX" (ici ce serait cExhange1).

    Citation Envoyé par passio Voir le message
    J'ai un :

    from API_exchange_1 import Api1
    from API_exchange_2 import Api2

    Puis :
    o1 = api1.v1
    Hum... Je n'aime pas ça car dans ton code, "api1" n'existe pas. Si tu avais écrit o1=Api1.v1 alors ok ce serait plus compréhensible. Et dans ce cas, v1 est un attribut statique de la classe Api1

    Citation Envoyé par passio Voir le message
    o2 = api2.constructeur()
    J'aime de moins en moins. Le constructeur de base d'un objet c'est "__init__()" et il est automatiquement appelé quand tu écris var=l_objet_en_question().
    Tu as parfaitement le droit d'appeler objet.methode() mais cette méthode n'est certainement pas un constructeur et amène là aussi encore de la confusion.
    D'ailleurs en y regardant mieux, vu ta façon de l'appeler, cette méthode semble être une méthode statique.
    Et évidemment en supposant là aussi que "api2" soit en réalité "Api2".

    Citation Envoyé par passio Voir le message
    exhange1 = Exchange1(o1, "BTC-USDT")
    Ok. Ou alors exhange1 = Exchange1(Api1.v1, "BTC-USDT")
    Citation Envoyé par passio Voir le message
    exchange1 et exchange2 sont des classes à presque entièrement similaires.
    Comme le disait M. Spock, deux choses égales à une troisième sont égales entre elles (version vulcaine de la transitivité). Peut-être tu aurais avantage à transformer ces classes de "presque entièrement similaires" à "totalement similaires" ce qui te permettrait d'en virer une des deux.
    Surtout que ce n'est pas parce qu'une classe est définie qu'elle est automatiquement figée. Certaines classes peuvent recevoir lors de l'instanciation des éléments qui leur permettront ensuite de produire des résultats adaptés.
    Exemple
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class c3D:
    	def __init__(self, base, h, *, pointe=False):
    		self.base=base
    		self.h=h
    	def volume(self): return self.base * self.h / (3 if pointe else 1)
     
    cube=c3D(25, 10)
    cone=c3D(25, 10, pointe=True)
    print(cube.volume())
    print(cone.volume())
    Comme tu le vois, une classe 3D qui sait calculer son volume. Or le volume d'un objet 3D diffère selon qu'il est droit (cube, cylindre) ou dit "en pointe" (chapeau de clown). Dans les deux cas le volume c'est "base * hauteur" mais pour ceux qui sont dits "en pointe" on divise ensuite par 3 (une sombre histoire d'intégrale à la con => autant j'adorais les dérivées, autant j'ai jamais pu piffer les intégrales)
    Ben mon objet sait faire la différence à condition que ce soit l'appelant qui le lui indique lors de l'instanciation.
    Peut-être t'inspirer de cet exemple pour tes propres objets...

    Citation Envoyé par passio Voir le message
    Voici une méthode qu'ils ont comme différence, les méthodes ont les mêmes (dans cet exemple, self.API est l'objet o donné au constructeur)
    En gros, cette méthode retourne une valeur qu'elle va chercher dans un Json retournée par la requête API.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    def get_best_book_bid(self):
    	return float(self.API.get_symbol_book(self.pair,4,1)[0]["data"]["buys"][0]["price"])
     
    def get_best_book_bid(self):
    	return float(self.API.get_orderbook(self.pair)["bid"][0]["rate"])
    Ca ressemble fortement à mon exemple...

    Citation Envoyé par passio Voir le message
    Comme les API pour accéder aux données des exchanges sont différentes, c'est la raison pour laquelle j'ai une classe par exchange.
    Et si à la place tu apprenais à ton (note bien le singulier) API à choisir comment accéder aux données selon les ordres que tu lui donnes à la création (ou même à l'appel de la méthode d'accès, pourquoi pas, dans mon c3D j'aurais parfaitement pu faire la différence dans la façon d'appeler la méthode "volume()"...)

    Ou alors un héritage. Un objet dit "de base" et ensuite deux sous-classes "cExhange1" et "cExhange2" qui bénéficieraient toutes deux des éléments de leur ancètre commun tout en ayant ensuite les méthode "get_best_book_bid" adaptées à leurs caractéristiques...

    Exemple avec mon objet 3D
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class c3D:
    	def __init__(self, base, h):
    		self.base=base
    		self.h=h
    	def volume(self): return self.base * self.h
     
    class c3D_pointe(c3D):
    	def volume(self): return super().volume() / 3
     
    cube=c3D(25, 10)
    cone=c3D_pointe(25, 10)
    print(cube.volume())
    print(cone.volume())
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2011
    Messages
    49
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 49
    Par défaut
    Merci Sve@r ! Merci pour ce temps consacré à formuler cette explication. Merci aussi pour les exemples et la syntaxe. Je vais essayer de m'en inspirer pour améliorer mon code. C'est difficile de faire un choix.
    Effectivement, c'était bien o1 = Api1.
    Je n'utilise pas ces noms, mais je les ai changé pour ne pas faire de publicité aux plateformes.

    En avançant, j'étais entrain de coder une méthode refresh_data() des classes Exchange, et effectivement, il y a trop de code en doublon, je vais essayer une de tes solutions, ta réponse était super clair et précise, merci !!!!

    Je pourrais donc aussi tout simplement fournir en argument à l'instanciation d'un exchange un str avec le nom de l'exchange, et utiliser certaines méthodes par rapport à ce str ?

    Au sujet de l'étoile * seule en argument de ta méthode, je n'ai jamais étudié cela. Je n'arrive pas à trouver d'autres exemples, et je ne vois pas de quelle manière cet argument * est utile à ta méthode ?

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 835
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par passio Voir le message
    Je pourrais donc aussi tout simplement fournir en argument à l'instanciation d'un exchange un str avec le nom de l'exchange, et utiliser certaines méthodes par rapport à ce str ?
    On peut fournir ce qu'on veut à l'instanciation. Sauf qu'un str oui ok mais ensuite? Il faut transformer ce str en objet et comment faire cela?
    Exemple: j'appelle mon objet avec "int" => toto=mon_objet("int")? Comment ensuite mon_objet pourra faire le lien entre la string "int" et le fait qu'il faut générer un int?
    Certes il y a des possibilités évidemments. On peut par exemple se baser sur des "if" en rafale (if s = "int": truc=int(...)) et etc. On peut aussi utiliser eval() qui convertit une string en instruction Python (s="int"; a=eval("%s(5)" % s); print(a) => donnera 5).
    Mais c'est 1) compliqué et 2) inutile (et en plus 3) les programmeurs Python n'aiment pas utiliser eval() car 99 fois sur 100 ça traduit un problème de conception).
    N'oublie pas qu'un objet est manipulable. Tu peux très bien passer un objet en tant que paramètre d'un autre objet
    Exemple
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def makeType(obj, v):
    	return obj(v)
     
    a=makeType(int, 5)
    b=makeType(float, 6)
    c=makeType(str, 7)
    print(a, type(a))
    print(b, type(b))
    print(c, type(c))

    Et la fonction peut même accepter n paramètres pour plus de souplesse
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def makeType(obj, *v):
        return obj(*v)
     
    class cA:
        def __init__(self, x):
            self.x=x
     
    class cB:
        def __init__(self, x, y):
            self.x=x
            self.y=y
     
    a=makeType(cA, 5)
    print(a, type(a), a.x)
     
    b=makeType(cB, 5, 6)
    print(b, type(b), b.x, b.y)
    Plus de détails sur la signification de cette étoile devant le paramètre "v" ici.

    Citation Envoyé par passio Voir le message
    Au sujet de l'étoile * seule en argument de ta méthode, je n'ai jamais étudié cela. Je n'arrive pas à trouver d'autres exemples, et je ne vois pas de quelle manière cet argument * est utile à ta méthode ?
    Ah oui, ça c'est rien, c'est une de mes habitudes.
    Ca force juste l'appelant à nommer explicitement tous les paramètres qui suivent l'étoile lors de l'appel
    def fct(a, *, b): ... tu peux appeler au choix fct(2, b=3) ou bien fct(a=2, b=3) ou même fct(b=3, a=2) mais tu ne peux pas appeler fct(2, 3).
    Plus de détails ici.
    J'aime bien un peu me forcer à nommer explicitement mes paramètres, surtout si ces paramètres sont (comme dans mon exemple) facultatifs. Ca réduit la possibilité de faire des erreurs lors de l'appel...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

Discussions similaires

  1. Comment bien programmer en PHP ?
    Par Community Management dans le forum Langage
    Réponses: 257
    Dernier message: 01/12/2014, 15h48
  2. Réponses: 2
    Dernier message: 13/05/2014, 20h05
  3. Bien programmer une classe avec sa gestion d'erreur
    Par chris81 dans le forum Framework .NET
    Réponses: 8
    Dernier message: 13/02/2007, 18h13
  4. Comment bien programmer en C ?
    Par lastrecrue dans le forum C
    Réponses: 14
    Dernier message: 12/07/2006, 12h44

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo