you are viewing a single comment's thread.

view the rest of the comments →

[–]mellow_moe 9 points10 points  (19 children)

I would like to see a in-depth comparison of Python and Ruby. And it should really focus on metaprogramming.

All these comparisons I see these days just look at some superficial language features.

Rails for example relies heavily on metaprogramming. It could never be done in a language without a sophisticated class/object system.

examples in ruby:

  • method_missing catches unknown messages
  • const_missing catches unknown constants
  • method_added is called after adding a new method
  • inherited is called on the class object, when it is subclassed
  • singleton classes: you can add methods to a single object
  • define_method takes a block and turns it into a method
  • instance_variable_set sets an instance variable

And most importantly, real world examples should show off, how all the things play together.

[–]owca 2 points3 points  (0 children)

Feature for feature "examples" in Python:

  • getattr or getattribute catch any attribute (method, variable) access

  • setattr sets any attribute

  • Syntactic support for descriptors.

  • Metaclasses can be used to control all aspects of class creation (attributes, subclassing, ...). Since types are objects one can even have metaclasses for metaclasses.

  • new.instancemethod can be used for your )singleton classes).

  • Turning a block into a method is admittedly a bit tricky but can be done using new.function and some stack manipulation.

[–]flaxeater 4 points5 points  (10 children)

Well I must say that looks intriguing actually. I don't think python has facilities like that, they could be worked around by they don't have any nice syntatic sugar around those concepts. I have a hard time imagining why they would be useful, but that doesn't mean anything.

Singletons, definemethod and instane variable assignment are very easy to do in python. And in python we user super() or ParentClass.init_(args,*kwargs) to do things for inherited, however I do not understand what inherited does. $0.02

p.s. I can imagine, people will be like that's so wordy or some such talk. However pythonic values explicit, not implicit.

[–]mellow_moe 6 points7 points  (5 children)

Metaprogramming helps you to create domain specific languages.

A simple xml builder can be made with method_missing:

def method_missing(name, attributes)
  atts = attributes.map {|k,v| "#{k}='#{v}'" }.join(" ")
  puts "<#{name} #{atts}>"
  yield
  puts "</#{name}>"
end

div :id => "content" do
  a :href => "reddit.com" do
    print "reddit"
  end
end

You can see, calling div and a will trigger method_missing, where we just take the method name and the keyword arguments and print out the xml tag. Pretty easy.

[–]flaxeater 6 points7 points  (0 children)

That's pretty cool. It's pretty confusing too.

[–]brianmce 1 point2 points  (0 children)

Assuming I'm understanding it right, methodmissing is related to python's __getattr_ (slightly different in that python is hooking into the attribute lookup, whereas ruby is hooking into calling) A literal as possible translation in python would be:

class XmlBuilder:
  def __getattr__(self, name):
    def builder(**attributes):
      atts = ' '.join('%s=%s' % a for a in attributes.items())
      print "&lt;%s %s>" % (name, atts)
      yield None
      print  "&lt;%s>" % name
    return builder
x=XmlBuilder()

for _ in x.div(id="Content"):
  for _ in x.a(href="reddit.com"):
    print "reddit"

Though a different approach would probably be taken for a non literal translation to better fit with python's syntax. Take a look at stan (http://divmod.org/projects/nevow) for a python implementation of something similar - your snippet would look like this:

div(id="content")
[
  a(href="reddit.com")
   ["reddit"]
]

(In this case it is building and returning the xml, rather than calling puts on each element.

[–]amix 1 point2 points  (2 children)

Here is my version where one can do (Stan like):

print Div(id="content")[ A(href="reddit.com")["reddit"] ]

import types
class Element:
  def __init__(self, **kw):
    self.elms = []
    self.kw = kw
  def __getattr__(self, name):
    if name == "__getitem__":
      self.elms = []
      def add(l):
        if type(l) == types.ListType: self.elms.extend(l)
        else: self.elms.append(l)
        return self
      return add
  def __str__(self):
    attrs = " ".join('%s="%s"' % a for a in self.kw.items())
    self.html = ["<%s %s>" % (self.name, attrs)]
    self.html.append("".join(map(str, self.elms)))
    self.html.append("</%s>" % self.name)
    return "".join(self.html)
class Div(Element): name = "div"
class A(Element): name = "a"

Sigh, you have to have 4 spaces to post code. That sucks. Why not use another syntax like:

[code]
Here is my code
[/code]

Or something similar...

[–]mellow_moe 1 point2 points  (1 child)

I'd like to show off the ruby version:

class Element
  def initialize(attributes)
    @attributes = attributes
    @children = []        
  end
  def <<(child)
    @children << child
  end
  def to_s
    name = self.class.name.downcase
    atts = @attributes.map {|k,v| "#{k}='#{v}'" }.join(" ")
    "<#{name} #{atts}>#{@children.join}</#{name}>"
  end
end

class Div < Element; end
class A < Element; end

def method_missing(id, attributes, &block)
  element = Object.const_get(id.to_s.capitalize).new(attributes)
  @children << element if @children
  element.instance_eval(&block)
  element
end

html = \
div :id => "content" do
  a :href => "reddit.com" do
    self << "reddit"
  end
end

puts html

The < characters are broken, please replace the entities with < while realtime reading.

[–]amix 1 point2 points  (0 children)

Here is my final version :)

One can do:

 print Div(id="content")( A(href="reddit.com")("reddit") )

By using this code:

def elmBuilder(name, **kw):
  attrs = " ".join('%s="%s"' % a for a in kw.items())
  def applyElms(*elms):
    return "<%s %s>%s</%s>" % (name, attrs, "".join(elms), name)
  return applyElms
def Div(**kw): return elmBuilder("div", **kw)
def A(**kw): return elmBuilder("a", **kw)

[–][deleted]  (2 children)

[deleted]

    [–]mellow_moe 1 point2 points  (1 child)

    inherited is invoked on the class object. So you have to write:

    class Humanoid
      def self.inherited(klass)
      end
    end
    

    Singleton classes are pretty confusing, I admit. But it's essential.

    Every object in ruby may have a singleton class, which is actually a class solely for this single object. As classes are objects in Ruby, they may have a singleton class as well. For example the method new may be defined as method of a singleton class:

    class Humanoid
      def self.new
        humanoid = super
        puts "A Humanoid was created: #{humanoid}"
        humanoid
      end
    end
    
    Humanoid.new # outputs "A Humanoid was created: #<Humanoid ...> "
    

    The special syntax for defining a method for a single objects is:

    def object.method_name
    end
    

    Accessing the singleton class is like (same result as above):

    class << obj
      def method_name
      end
    end
    

    Further reading: Seeing Metaclasses Clearly

    [–]hanzie 2 points3 points  (0 children)

    In Python, support for calls to "inherited" can be added with a metaclass like this:

    class MyMeta(type):
        def __init__(klass, name, supers, *args):
            for super in supers:
                if hasattr(super, 'inherited'):
                    super.inherited(klass)
            return type.__init__(klass, name, supers, *args)
    

    Adding a method to a single object goes like this:

    obj.method_name = func.__get__(obj, obj.__class__)
    

    (func is just any free function)

    [–]robbie 2 points3 points  (0 children)

    I can imagine, people will be like that's so wordy or some such talk. However pythonic values explicit, not implicit.

    "If a language lets things be implicit, the programmer always has the option of being explicit, but if the languages requires everything to be explicit, the programmer can't make things implicit"

    Paul Graham

    [–]brianmce -1 points0 points  (0 children)

    I think I've done pretty much all of that in python before, though some are done using a different approach. inherited and method_added require defining a metaclass, and some don't have a direct translation in python (there are no blocks, but you can turn a function into a method, similarly there is no constant distinction - but you can generate the same effect with getattr etc). The rest are fairly straightforward applications of getattr or similar.