Ruby <-> Python

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
Zambo
User
Beiträge: 9
Registriert: Montag 11. September 2006, 06:48
Wohnort: Düsseldorf
Kontaktdaten:

Montag 11. September 2006, 10:00

Also zu Punkt 1 und 4 muß ich euch recht geben. Das geht in Python. Mal abgesehen davon, dass mir der Ruby-Code besser gefällt. Aber darum geht es nicht.

Das mit den Blöcken hat BlackJack schon zugegeben, dass das in Python so nicht möglich ist. Was natürlich schon für sich alleine ein Grund seien kann, Ruby vorzuziehen.

Aber bei den Mix-Ins muß ich doch widersprechen. Natürlich kann man Mix-Ins über Mehrfachvererbung realisieren. Aber das Konzept geht bei Ruby doch um einiges weiter, als es bei Python möglich ist. Zum Beispiel möchte ich schon gerne wissen, ob dies in Python auch so möglich wäre?

Code: Alles auswählen

module Summable
   def sum
       inject { |v,n| v+n }
   end
end

class Array
   include Summable
end

class Range
   include Summable
end

[1,2,3,4,5].sum      ->  15
('a'..'m').sum       -> "abcdefghijklm"
Nebenbei, was großes soll man auch nicht in den Beispielen entdecken. Es soll nur ein paar Konzepte aufzeigen, die ich in Ruby besonders gelungen finde.

mawe hat geschrieben:Hi!
1.) Es ist z.B. ohne Probleme möglich, während der Laufzeit, Methoden zu einer Klasse hinzuzufügen.
Mich würde mal interessieren: Wann braucht man das? Hat das schon mal jemand verwendet? IMO wird der Code dadurch unübersichtlich (weil der Code der Klasse verstreut wird), oder übersehe ich einen gravierenden Vorteil?

Gruß, mawe
Also ich habe das mal benutzt, um eine Webanwendung während sie lief upzudaten.

Gruß Zambo
Zuletzt geändert von Zambo am Montag 11. September 2006, 10:26, insgesamt 2-mal geändert.
BlackJack

Montag 11. September 2006, 10:22

mawe hat geschrieben:
1.) Es ist z.B. ohne Probleme möglich, während der Laufzeit, Methoden zu einer Klasse hinzuzufügen.
Mich würde mal interessieren: Wann braucht man das? Hat das schon mal jemand verwendet? IMO wird der Code dadurch unübersichtlich (weil der Code der Klasse verstreut wird), oder übersehe ich einen gravierenden Vorteil?
Das ist eine nette Lösung wenn man einen Haufen Funktionen/Methoden hat, die man zur Laufzeit auf Grundlage von irgendwelchen externen Daten zu Klassen zusammensetzen möchte. Dann würde das Klassenobjekt selbst aber auch dynamisch erzeugt.

Beispiel wäre ein Spiel bei dem Elemente wie Spieler, Fahrzeuge, Gebäude etc. jeweils Durch eine Klasse repräsentiert werden, die einzelnen Funktionen/Fähigkeiten unabhängig definiert werden und dann per Code aus einer Beschreibungsdatei, z.B. in XML, Klassen zusammengestellt werden.

Zugegebenermassen ein sehr seltener Anwendungsfall. Allgemein, kann man sagen, man kann diese Technik anwenden wo man in anderen (statischeren) Sprachen zu einem Code-Generator greifen würde.
BlackJack

Montag 11. September 2006, 11:12

Zambo hat geschrieben:Das mit den Blöcken hat BlackJack schon zugegeben, dass das in Python so nicht möglich ist. Was natürlich schon für sich alleine ein Grund seien kann, Ruby vorzuziehen.
Das ist aber nur ein kosmetischer Unterschied. Man kann halt keine anonymen Blöcke definieren, na und, dann gibt man dem Kind halt einen Namen und definiert eine lokale Funktion.
Aber bei den Mix-Ins muß ich doch widersprechen. Natürlich kann man Mix-Ins über Mehrfachvererbung realisieren. Aber das Konzept geht bei Ruby doch um einiges weiter, als es bei Python möglich ist. Zum Beispiel möchte ich schon gerne wissen, ob dies in Python auch so möglich wäre?

Code: Alles auswählen

module Summable
   def sum
       inject { |v,n| v+n }
   end
end

class Array
   include Summable
end

class Range
   include Summable
end

[1,2,3,4,5].sum      ->  15
('a'..'m').sum       -> "abcdefghijklm"
Das sieht aus nach "zur Laufzeit Basisklassen hinzufügen". Das geht grundsätzlich schon:

Code: Alles auswählen

In [44]: class A:
   ....:     pass
   ....:

In [45]: class B:
   ....:     def test(self):
   ....:         print 'b'
   ....:

In [46]: a = A()

In [47]: a.test()
---------------------------------------------------------------------------
exceptions.AttributeError     Traceback (most recent call last)

/home/marc/<ipython console>

AttributeError: A instance has no attribute 'test'

In [48]: A.__bases__ += (B,)

In [49]: a.test()
b
Allerdings kann man bei Typen die in C implementiert sind, entscheiden ob die auf diese Weise verändert werden dürfen und das ist bei den eingebauten Typen ist nicht der Fall.

Und das wird in Python auch üblicherweise nicht so gemacht. Wie mawe schon sagte, man zersplittert so die Definition von Klassen.

Wenn man ein generisches Summieren haben möchte, dann definiert man eine entsprechende Funktion.

Code: Alles auswählen

import operator

NO_DEFAULT = object()

def my_sum(iterable, default=NO_DEFAULT):
    iterator = iter(iterable)
    try:
        first = iterator.next()
    except StopIteration:
        if default is NO_DEFAULT:
            raise TypeError('empty iterable without default')
        else:
            return default
    return reduce(operator.add, iterator, first)

print my_sum(('H', 'a', 'l', 'l', 'o')) # -> 'Hallo'
print my_sum(xrange(6))                 # -> 15
print my_sum([], 0.0)                   # -> 0.0
print my_sum('')                        # Raises Exception.
Nebenbei, was großes soll man auch nicht in den Beispielen entdecken. Es soll nur ein paar Konzepte aufzeigen, die ich in Ruby besonders gelungen finde.
Du hast gesagt Dir fehlen ein paar Dinge in Python, die in Ruby möglich sind und es stellte sich heraus, das vieles davon in Python auch möglich ist.
Zambo
User
Beiträge: 9
Registriert: Montag 11. September 2006, 06:48
Wohnort: Düsseldorf
Kontaktdaten:

Montag 11. September 2006, 11:53

BlackJack hat geschrieben:
Zambo hat geschrieben:Das mit den Blöcken hat BlackJack schon zugegeben, dass das in Python so nicht möglich ist. Was natürlich schon für sich alleine ein Grund seien kann, Ruby vorzuziehen.
Das ist aber nur ein kosmetischer Unterschied. Man kann halt keine anonymen Blöcke definieren, na und, dann gibt man dem Kind halt einen Namen und definiert eine lokale Funktion.
Es ist einfach eine Frage des Geschmacks. Durch die Blöcke wird meiner Meinung nach der Code besser lesbar und dadurch natürlich leichter zu warten. Aber man muß es natürlich nicht verwenden. Jeder wie er will. Natürlich kann man kann man alles was man in Ruby programmiert auch in Python machen. Das ist doch klar. Aber um es überspitzt zu sagen; Man kann auch alles in Assembler programmieren. Aber wer will das schon.
Aber bei den Mix-Ins muß ich doch widersprechen. Natürlich kann man Mix-Ins über Mehrfachvererbung realisieren. Aber das Konzept geht bei Ruby doch um einiges weiter, als es bei Python möglich ist. Zum Beispiel möchte ich schon gerne wissen, ob dies in Python auch so möglich wäre?

Code: Alles auswählen

module Summable
   def sum
       inject { |v,n| v+n }
   end
end

class Array
   include Summable
end

class Range
   include Summable
end

[1,2,3,4,5].sum      ->  15
('a'..'m').sum       -> "abcdefghijklm"
Das sieht aus nach "zur Laufzeit Basisklassen hinzufügen". Das geht grundsätzlich schon:

Code: Alles auswählen

In [44]: class A:
   ....:     pass
   ....:

In [45]: class B:
   ....:     def test(self):
   ....:         print 'b'
   ....:

In [46]: a = A()

In [47]: a.test()
---------------------------------------------------------------------------
exceptions.AttributeError     Traceback (most recent call last)

/home/marc/<ipython console>

AttributeError: A instance has no attribute 'test'

In [48]: A.__bases__ += (B,)

In [49]: a.test()
b
Allerdings kann man bei Typen die in C implementiert sind, entscheiden ob die auf diese Weise verändert werden dürfen und das ist bei den eingebauten Typen ist nicht der Fall.

Und das wird in Python auch üblicherweise nicht so gemacht. Wie mawe schon sagte, man zersplittert so die Definition von Klassen.

Wenn man ein generisches Summieren haben möchte, dann definiert man eine entsprechende Funktion.

Code: Alles auswählen

import operator

NO_DEFAULT = object()

def my_sum(iterable, default=NO_DEFAULT):
    iterator = iter(iterable)
    try:
        first = iterator.next()
    except StopIteration:
        if default is NO_DEFAULT:
            raise TypeError('empty iterable without default')
        else:
            return default
    return reduce(operator.add, iterator, first)

print my_sum(('H', 'a', 'l', 'l', 'o')) # -> 'Hallo'
print my_sum(xrange(6))                 # -> 15
print my_sum([], 0.0)                   # -> 0.0
print my_sum('')                        # Raises Exception.
Also was du an Code geschrieben hast, ist eher ein Argument für Ruby :wink: Nicht böse nehmen :D
Aber Ruby ist doch um einiges leichter zu lesen und zu verstehen. Was du da für einen Aufwand treiben mußt ist schon jenseits von gut und böse.

Nebenbei, was großes soll man auch nicht in den Beispielen entdecken. Es soll nur ein paar Konzepte aufzeigen, die ich in Ruby besonders gelungen finde.
Du hast gesagt Dir fehlen ein paar Dinge in Python, die in Ruby möglich sind und es stellte sich heraus, das vieles davon in Python auch möglich ist.
Ja, die Hälfte. Der Rest geht entweder nicht oder nur durch einen so hohen Aufwand, dass die Verwendung von Phython kaum zu rechtfertigen ist.

Aber um die Sache zu beenden. Ich denke, die Leute sollen sich selbst ein Urteil bilden. Die Fakten stehen hier in diesen Beiträgen und jeder kann selbst entscheiden was für ihn wichtiger ist. Ich denke, das Ruby und Python tolle Programmiersprachen sind. Zumindest im Vergleich zu Java oder Pascal. 8)
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Montag 11. September 2006, 12:28

mawe hat geschrieben:Mich würde mal interessieren: Wann braucht man das? Hat das schon mal jemand verwendet? IMO wird der Code dadurch unübersichtlich (weil der Code der Klasse verstreut wird), oder übersehe ich einen gravierenden Vorteil?
Das mach IMHO zum Beispiel dann Sinn, wenn du in deinem Programm ein externes Modul verwendest und dem einfach eine Sinnvolle Methode noch fehlt. Nun kann man es einfach hinzufügen, ohne das Ursprüngliche Modul zu "patchen"...
Wobei man das meist durch einfaches Erben erledigen kann...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Montag 11. September 2006, 14:05

Zambo hat geschrieben:
BlackJack hat geschrieben:

Code: Alles auswählen

import operator

NO_DEFAULT = object()

def my_sum(iterable, default=NO_DEFAULT):
    iterator = iter(iterable)
    try:
        first = iterator.next()
    except StopIteration:
        if default is NO_DEFAULT:
            raise TypeError('empty iterable without default')
        else:
            return default
    return reduce(operator.add, iterator, first)

print my_sum(('H', 'a', 'l', 'l', 'o')) # -> 'Hallo'
print my_sum(xrange(6))                 # -> 15
print my_sum([], 0.0)                   # -> 0.0
print my_sum('')                        # Raises Exception.
Also was du an Code geschrieben hast, ist eher ein Argument für Ruby :wink: Nicht böse nehmen :D
Aber Ruby ist doch um einiges leichter zu lesen und zu verstehen. Was du da für einen Aufwand treiben mußt ist schon jenseits von gut und böse.
Okay, man muss diesen Aufwand nicht treiben. Meine Funktion macht mehr als Deine. Sie kommt nämlich auch damit klar wenn eine leeres iterierbares Objekt übergeben wurde und gibt dann entweder den `default` Wert zurück oder löst eine Ausnahme aus falls es den nicht gibt. Eine direkte Umsetzung Deiner Funktion sähe so aus:

Code: Alles auswählen

import operator

def my_sum(iterable):
    return reduce(operator.add, iterable, None)
Wie sähe die Ruby-Umsetzung von meinem ursprünglichen `my_sum` aus?
Zambo
User
Beiträge: 9
Registriert: Montag 11. September 2006, 06:48
Wohnort: Düsseldorf
Kontaktdaten:

Montag 11. September 2006, 15:08

Also mal abgesehen davon, das ich es nicht so machen würde:

Code: Alles auswählen

def my_sum(iterable)
  iterable.inject { |v,n| v+n }
end
  
print my_sum(['H', 'a', 'l', 'l', 'o'])       # -> 'Hallo' 
print my_sum(1..5)                            # -> 15
print my_sum([])                              # -> nil 
print my_sum('')                              # -> nil
Wie du siehst, entsteht bei Ruby überhaupt keine Fehlermeldung, die abgefangen werden muß. Wenn man nichts übergibt, kommt auch nichts raus :D

Der Code ist trotzdem "etwas" übersichtlicher.

Gruß
Zambo
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Montag 11. September 2006, 16:28

Mal abgesehen davon das mich END Blöcke stören (dann doch lieber die hier { … } wie bei C/C++) scheint nach deinen Beilspielen Ruby schon nicht schlecht zu sein. Ich hab mal vor einiger zeit mal ein Ruby Tutorial angeschaut und bin dann zu dem Ergebnis gekommen das vieles (für mich) zu unübersichtlich ist. Hat mich teilweise doch schon an Perl erinnert, oder besser gesagt viele Möglichkeiten sind genauso unstrukturiert, wirr und machen den Code auf Dauer schlecht wartbar (also für mich jetzt!).

Ich finde jeder sollte selber entscheiden welche Programmiersprache er Hauptsächlich nutzen will. Für mich viel halt die Auswahl auf Python weil der Code sehr Ansprechend aussieht für mich und leicht zu erlernen ist. Ganz früher hatte ich mal in C/C++ Programmiert und muss heute sagen, warum habe ich nicht schon früher in Python geschrieben :) Naja heute würde ich C/C++ (eher C ^^) bei Zeitkritischen Sachen nutzen und ansonsten alles in Python machen bzw. je nach Anwendungsfall auch mal Perl benutzen. (-> wenns schnell gehen soll und dabei Reguläre ausdrücke zum Einsatz kommen, was in Python mMn zu Kompliziert gelöst ist bzw. ich verstehe die Umsetzung in Python noch nicht wirklich).

lg
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Montag 11. September 2006, 17:03

Zambo hat geschrieben:2.) Es gibt Mix-Ins. Das heißt, man schreibt eine Funktion und kann diese zu einer Klasse hinzumischen.
Geht mit Mehrfachvererbung oder...:

Code: Alles auswählen

>>> import sys
>>> def include(obj):
...  ns = sys._getframe(1).f_locals
...  for key, value in obj.__dict__.iteritems():
...   if not key.startswith('_'):
...    ns[key] = value
... 
>>> class Debug(object):
...  def wer_bin_ich(self):
...   return self.__class__.__name__
... 
>>> class MyClass(object):
...  include(Debug)
... 
>>> MyClass().wer_bin_ich()
'MyClass'
:P

@Thema: auf alle Fälle Python. Wegen logischer Syntax, Mehrfachvererbung, Unicode, Dekoratoren, einfachen Closures, First-Class Functions and noch viel mehr :D (Man kann zb den () Operator überladen ^^)

//EDIT: http://lucumr.pocoo.org/entry/rrrrruby/ <-- meine persönliche: warum ich ruby nicht wirklich leiden kann liste :D
TUFKAB – the user formerly known as blackbird
BlackJack

Montag 11. September 2006, 17:49

Zambo hat geschrieben:Also mal abgesehen davon, das ich es nicht so machen würde:

Code: Alles auswählen

def my_sum(iterable)
  iterable.inject { |v,n| v+n }
end
  
print my_sum(['H', 'a', 'l', 'l', 'o'])       # -> 'Hallo' 
print my_sum(1..5)                            # -> 15
print my_sum([])                              # -> nil 
print my_sum('')                              # -> nil
Wie du siehst, entsteht bei Ruby überhaupt keine Fehlermeldung, die abgefangen werden muß. Wenn man nichts übergibt, kommt auch nichts raus :D
Äh, genau darum ging es doch aber. Das man einen Defaultwert übergeben kann und das eine Ausnahme ausgelöst wird wenn die Sequenz leer ist und kein Defaultwert übergeben wurde. Das Ergebnis da hat Deine alte Version auch produziert und ist nicht mit meiner ersten Version äquivalent.

Mal angenommen ich summiere eine Liste die Zahlen enthalten soll und die ist leer; dann möchte ich zur Weiterverarbeitung entweder eine 0 haben, oder eine Ausnahme die mich drauf aufmerksam macht; aber doch nicht `nil`. Spätestens wenn ich damit weiterrechnen will, bekomme ich eine Ausnahme.
Der Code ist trotzdem "etwas" übersichtlicher.
Was nun aber höchstgradig Geschmackssache ist. Sowohl `reduce` als auch `inject` sind nicht wirklich selbsterklärend.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Montag 11. September 2006, 18:12

blackbird hat geschrieben: //EDIT: http://lucumr.pocoo.org/entry/rrrrruby/ <-- meine persönliche: warum ich ruby nicht wirklich leiden kann liste :D
Ja genau, das mein ich mit wirren unübersichtlichen code ^^ Sieht irgendwie eigenartig aus xD
Zambo
User
Beiträge: 9
Registriert: Montag 11. September 2006, 06:48
Wohnort: Düsseldorf
Kontaktdaten:

Montag 11. September 2006, 20:02

Also wenn du lieber eine Null zurück kriegen willst, könnte ich folgenden Code anbieten:

Code: Alles auswählen

def my_sum(iterable) 
  ergebnis = iterable.inject { |v,n| v+n } 
  return 0 if ergebnis.nil?
  return ergebnis
end 
oder

Code: Alles auswählen

def my_sum(iterable) 
  ergebnis = iterable.inject { |v,n| v+n } 
  return ergebnis.nil? ? 0 : ergebnis
end  
Was nun aber höchstgradig Geschmackssache ist. Sowohl `reduce` als auch `inject` sind nicht wirklich selbsterklärend
Das stimmt natürlich :wink:
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Dienstag 12. September 2006, 06:41

Zambo hat geschrieben:

Code: Alles auswählen

...
  return 0 if ergebnis.nil?
  return ergebnis
...
  return ergebnis.nil? ? 0 : ergebnis
Wow :shock:

Also die beiden Varianten sind ja der Hammer und das meine ich nur negativ 8)

Also 'return 0 if ergebnis.nil?' Erscheind mir noch einigermaßen logisch, wenn es so aussehen würde: 'return 0 if ergebnis == nil'
Aber die zweite Variante ist echt undurchsichtig!

In Python würde das einfach so aussehen:

Code: Alles auswählen

if ergebnis == "nil":
    return 0
else:
    return ergebnis
Da ist es doch jedem klar, was passiert, selbst wenn einer keine Ahnung von Python hat.
OK man könnte auch in Python einen undurchsichtigen Einzeiler machen...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Zambo
User
Beiträge: 9
Registriert: Montag 11. September 2006, 06:48
Wohnort: Düsseldorf
Kontaktdaten:

Dienstag 12. September 2006, 06:52

Die zweite Version sollte nur zeigen, das es auch kürzer geht. Ich komme ja eigentlich aus der C++ Ecke und da gibts auch so Konstruktionen. Auch wenn ich sie ehrlich gesagt noch nie benutzt habe :wink:

Aber natürlich kann man es auch so schreiben:

Code: Alles auswählen

def my_sum(iterable) 
  ergebnis = iterable.inject { |v,n| v+n } 
  return 0 if ergebnis==nil
  return ergebnis
end
oder so

Code: Alles auswählen

def my_sum(iterable) 
  ergebnis = iterable.inject { |v,n| v+n } 
  if ergebnis==nil then
    return 0
  else
    return ergebnis
  end
end 
Ruby läßt da einen sehr viele Möglichkeiten.
Benutzeravatar
jens
Moderator
Beiträge: 8461
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Dienstag 12. September 2006, 07:17

Das sieht schon Augenfreundlicher aus :)
Aber das ist andererseits genau das, warum ich Python schöner finde. Vergleichen wir mal direkt:

Code: Alles auswählen

if ergebnis == nil then
    return 0
else
    return ergebnis
end

Code: Alles auswählen

if ergebnis == "nil":
    return 0
else:
    return ergebnis
Wie man sehen kann, sieht beides ähnlich aus. Aber der Python Code ist weniger, aber gleichgut zu lesen. "then" ist einfach nur der Doppelpunkt und "end" wird gleich eingespart, dafür haben wir ja Eingerückt...

Wobei Ruby da auch schon sehr gut ist: Die Blöcke werden zumindest nicht, wie in C/Java/JavaScript mit { und } gebildet, Zeilenenden benötigen auch kein Semikolon. Das ist schon mal was!

Also würde ich mal sagen, Python ist die schönste Sprache, danach kommt Ruby und dann, mit weitem Abstand, der Rest :)

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten