2

I have this code in ruby:

class Package

def initialize(name)
    @name = name
    @elements = []
end

[:type, :block].each do |bindtype|
    define_method "get_#{bindtype}_by_name" do |name|
        get_by_name(name, bindtype)
    end
end

def get_by_name(name, bindtype=nil)
    @elements.each do |element|
        return if element.name == name
    end
    return nil
end

I've read this question about define_method in python and I wanted to implement the same thing in Python with my Ruby code:

bindtypes = {"type", "block"}

class Package:

    def __init__(self, name):
        self.name = name
        self.elements = list()

    def get_by_name(self, name, bindtype=None):
        for element in self.elements:
            if element.name == name:
                return element
        return None

for bindtype in bindtypes:
    def _in_bindtype(self, bindtype, bindtype):
        Package.get_by_name(bindtype, bindtype)
    setattr(Package, "get_"+bindtype+"_by_name", _in_bindtype

I call the function like this:

package = Package("package")    
block = Block("blockName")
package.elements.append(block)
blockFound = package.get_block_by_name(block.name, block.bindtype)

But obviously this doesn't work because I don't recover the name of the element that I'm searching. Can someone help me understand better how this python define_method works with my code?

Community
  • 1
  • 1
user3314570
  • 237
  • 4
  • 14
  • Instead of using `list()` to create an empty list object, use `[]`. See [Why is \[\] faster than list()?](https://stackoverflow.com/q/30216000) – Martijn Pieters Jun 27 '16 at 18:36

1 Answers1

3

You were almost there; bind the bindtype value as a default value for the function argument, and you want to call get_by_name() on self:

for bindtype in bindtypes:
    def _in_bindtype(self, name, bindtype=bindtype):
        self.get_by_name(name, bindtype)
    setattr(Package, "get_{}_by_name".format(bindtype), _in_bindtype)

I took the liberty of using str.format() to format the method name rather than use + concatenation, it is more readable I find.

Alternatively, in Python 3 you could just create functools.partialmethod() objects:

from functools import partialmethod

for bindtype in bindtypes:
    setattr(Package, "get_{}_by_name".format(bindtype),
            partialmethod(Package.get_by_name, bindtype=bindtype))
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks for your time! I think I have explained poorly my problem. I call my function like this for example `get_block_by_name("BlockName", "block")`, so my parameters are different. I wrote `bindtype`everywhere because I didn't know have to retrieve the name "BlockName". Do you know how to retrieve the name? – user3314570 Jun 28 '16 at 08:51
  • @user3314570: yes, you are explaining it poorly, I fear. Perhaps you can update your question with an example of how you expect things to work? Give us input and expected output. But given the Ruby code you wrote and the question you linked to, my answer covers what you asked exactly. – Martijn Pieters Jun 28 '16 at 08:53
  • I found my problem. I juste had to replace `def _in_bindtype(self, bindtype, bindtype=bindtype):`and `self.get_by_name(bindtype, bindtype)` by `def _in_bindtype(self, name, bindtype=bindtype):`and `self.get_by_name(name, bindtype)`. Your response helped me understand how it works. Thanks! – user3314570 Jun 28 '16 at 09:09
  • Ah yes, I should have spotted that. I also cleaned up the `partialmethod` case as I had left a copy-and-paste error in there. – Martijn Pieters Jun 28 '16 at 09:13