pmontrasio / rubynights-20170301

2 stars 2 forks source link

Moduli e package #27

Closed cstrap closed 7 years ago

cstrap commented 7 years ago

In breve Modulo: qualsiasi script python che contiene classi e funzioni. L'import del modulo deve essere aggiunto al PYHONPATH, oppure risiedere nella directory corrente o ancora in un namespace (senza init) Package: il modo per creare un modulo importabile usando la notazione a punti. Si installa nella directory site-packages e viene esposto a tutto l'interprete, utilizzando la forma import module o from module import something.

Documentazione: https://docs.python.org/3.6/tutorial/modules.html

il vantaggio di utilizzare uno o l'altro rende il tuo script portabile, con la sola necessità dell'interprete. Es: bottle (una sorta di sinatra), rocket (web server)

pmontrasio commented 7 years ago

Ok, credo di aver capito il perché non avevo capito. Il concetto equivalente alla directory site-package è praticamente scomparso in Ruby. In sviluppo si usano i version manager come rvm per gestire più versioni dell'interprete (indispensabile perché si hanno clienti con progetti su versioni diverse). L'equivalente Python è virtualenv. Per ogni versione di Ruby si installa il package manager (sempre bundler ma possono volerci versioni diverse) con cui gestire insiemi diversi di gemme per progetto. Si può fare switch a mano o usare i gemset. Tipicamente le gemme finiscono da qualche parte dentro a ~/.rvm, separate per interprete e per gemset. Credo che virtualenv faccia anche questo. In produzione si fa un deploy con bundle nella directory del progetto, circa come fa npm. L'equivalente Ruby del package Python sembra essere la gemma, che però richiede un po' più di lavoro di packaging. Il file singolo Ruby non è niente, ma se ne può fare require ed usarne il contenuto. Se il suo contenuto è racchiuso in module end equivale ad un modulo Python ed ha un suo namespace. Non solo bisogna farne require, ma anche import. Qui è dove aiuta Rails, che fa da solo tutte le require e le import per i file in certe directory. Per distribuire i package e poterli scaricare con pip si usa il formato wheel?

cstrap commented 7 years ago

Per i package si può usare sia wheel che zip. I setuptools aiuta a pacchettizzare il tutto grazie ad un file chiamato setup.py. La differenza tra wheel e zip è che il nuovo formato wheel contiene i compilati C, quindi non si ha più la necessità di avere librerie esterne (es. il driver di postgres in psycopg) Nelle ultime versioni di pip prevale wheel. Tra l'altro non serve pip per installare un package, potrebbe essere banalmente scompattato, per poi essere installato con python setup.py install Nulla vieta di avere un package non pacchettizzato (zip o wheel), ma installabile dai sorgenti, l'importante è la presenza di setup.py. Si può far benissimo pip install https://github.com/nomepacchetto, pip install local/path ... ci sono un po' di opzioni. Una interessante è la -e che permette di installare il package in editing, ma sto andando OT, troppe informazioni.

Gli ambienti con virtualenv e rvm necessitano di una sezione a parte. Comunque sì, grazie al file requirements.txt (che può chiamarsi anche pippo o qualsivoglia, è solo convenzione) vengono riportati tutti i package, con versione, del progetto.

keobox commented 7 years ago

Molto semplicemente un package e' solo un gruppo di moduli python. Dentro uno stesso progetto posso definere piu' package e o moduli. Ad esempio se dovessi scrivere "Quake" in python farei un package "client", un package "server" e un package "Network" usato dagli altri 2. E' una astrazione che rinforza la modularizzazione. Per marcare una directory come package e' stato scelto di usare il file init.py, invece di dichiarare il package in ogni file di una certa directory, come fa Java.

pmontrasio commented 7 years ago

E come fa anche Ruby, ma limitatamente ai file che contengono module ... end

Java ha i package (la sua keyword è quella) là dove Python ha i moduli e sta introducendo i moduli come collezioni di package (Java 9).

Ruby ha solo moduli e li si usa per creare quelle astrazioni client, server, network nel modo che descrivevo, con un modulo che fa le veci del file __init__.py

cstrap commented 7 years ago

Aggiungo che un modulo, può utilizzare il famigerato:

if __name__ == '__main__':
    # do something

Utilizzando __name__ si fa in modo che il modulo venga azionato come script nel caso che venga invocato via interprete, ad esempio, python mymodule.py eseguirà il codice dentro l'if __name__..., mentre all'interno dell'interprete (o di altro modulo), ad esempio con un import mymodule, il codice all'interno dell'if __name__... non sarà eseguito, poiché __name__ non avrà il valore __main__ (riassumendo molto).

pmontrasio commented 7 years ago

In Ruby un file di cui si fa il require viene eseguito dall'inizio alla fine. Se definisce un modulo si può includervi la callback included che viene eseguita quando poi dell'altro codice farà include QuelModulo

module Foo
  def self.included(klass)
    puts "Foo has been included in class #{klass}"
  end
end

Googlando ho trovato l'equivalente di

if __name__ == '__main__':
    # do something

ed è l'orribile

if __FILE__== $0
  puts "sei il main!"
end

Di solito si organizzano i programmi in modo che non si debbano scrivere queste __brutture__: i moduli fanno i moduli e i main fanno i main.

Riassumendo

$ cat a.rb 
module A
  def self.included(k)
    puts "included"
  end
end

if __FILE__== $0
  puts "sei il main!"
end

$ irb
2.3.0 :001 > require "./a.rb"
 => true 
2.3.0 :002 > include A
included
 => Object 

$ ruby a.rb 
sei il main!