1

This following code works fine, and shows a way to create attributes and methods in execution time:

class Pessoa:
    pass
p = Pessoa( )
p.nome = 'fulano'

if hasattr(p, 'nome'):
    print(p) 
p.get_name = lambda self:'Sr.{}'.format(self.nome)

But, I think my way to create methods is not correct. There are another way to create a method dynamically ?

nickie
  • 5,608
  • 2
  • 23
  • 37
Silas Santiago
  • 545
  • 4
  • 9
  • what's with the backticks? Also, what is `x`? – Dimitris Fasarakis Hilliard Feb 02 '17 at 15:12
  • What is `x`? (Did you mean `p`?) Do you want to add the method [to the class](http://stackoverflow.com/questions/17929543/how-can-i-dynamically-create-class-methods-for-a-class-in-python) to be used by all objects or just [to a specific instance](http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object-instance)? – Steven Rumbalski Feb 02 '17 at 15:13
  • Somebody removed the back ticks and I just fixed the `x`, assuming it was a `p`. The first and third comment can be deleted. The second, pointing to better but longer answers than mine, should probably be edited. – nickie Feb 02 '17 at 15:35

4 Answers4

1

[Although this has really been answered in Steven Rumbalski's comment, pointing to two independent questions, I'm adding a short combined answer here.]

Yes, you're right that this does not correctly define a method.

>>> class C:
...   pass
...
>>> p = C()
>>> p.name = 'nickie'
>>> p.get_name = lambda self: 'Dr. {}'.format(self.name)
>>> p.get_name()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 1 argument (0 given)

Here's how you can call the function that is stored in object p's attribute called get_name:

>>> p.get_name(p)
'Dr. nickie'

For properly defining an instance method dynamically, take a look at the answers to a relevant question.

If you want to define a class method dynamically, you have to define it as:

>>> C.get_name = lambda self: 'Dr. {}'.format(self.name)

Although the method will be added to existing objects, this will not work for p (as it already has its own attribute get_name). However, for a new object:

>>> q = C()
>>> q.name = 'somebody'
>>> q.get_name()
'Dr. somebody'

And (obviously), the method will fail for objects that don't have a name attribute:

>>> r = C()
>>> r.get_name()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: C instance has no attribute 'name'
Community
  • 1
  • 1
nickie
  • 5,608
  • 2
  • 23
  • 37
1

There are two ways to dynamically create methods in Python 3:

  • create a method on the class itself: just assign a function to a member ; it is made accessible to all objects of the class, even if they were created before the method was created:

    >>> class A:          # create a class
        def __init__(self, v):
            self.val = v
    
    
    >>> a = A(1)             # create an instance
    >>> def double(self):    # define a plain function
        self.val *= 2
    
    >>> A.double = double    # makes it a method on the class
    >>> a.double()           # use it...
    >>> a.val
    2
    
  • create a method on an instance of the class. It is possible in Python 3 thanks to the types module:

    >>> def add(self, x):    # create a plain function
        self.val += x
    
    
    >>> a.add = types.MethodType(add, a)  # make it a method on an instance
    >>> a.add(2)
    >>> a.val
    4
    >>> b = A(1)
    >>> b.add(2)                 # chokes on another instance
    Traceback (most recent call last):
      File "<pyshell#55>", line 1, in <module>
        b.add(2)
    AttributeError: 'A' object has no attribute 'add'
    >>> type(a.add)               # it is a true method on a instance
    <class 'method'>
    >>> type(a.double)
    <class 'method'>
    

A slight variation on method 1 (on class) can be used to create static or class methods:

>>> def static_add(a,b):
    return a+b

>>> A.static_add = staticmethod(static_add)
>>> a.static_add(3,4)
7
>>> def show_class(cls):
    return str(cls)

>>> A.show_class = classmethod(show_class)
>>> b.show_class()
"<class '__main__.A'>"
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

Here is how I add methods to classes imported from a library. If I modified the library I would lose the changes at the next library upgrade. I can't create a new derived class because I can't tell the library to use my modified instance. So I monkey patch the existing classes by adding the missing methods:

# Import the standard classes of the shapely library
import shapely.geometry

# Define a function that returns the points of the outer 
# and the inner polygons of a Polygon
def _coords_ext_int_polygon(self):
    exterior_coords = [self.exterior.coords[:]]
    interior_coords = [interior.coords[:] for interior in self.interiors]
    return exterior_coords, interior_coords

# Define a function that returns the points of the outer 
# and the inner polygons of a MultiPolygon
def _coords_ext_int_multi_polygon(self):
    if self.is_empty:
        return [], []
    exterior_coords = []
    interior_coords = []
    for part in self:
        i, e = part.coords_ext_int()
        exterior_coords += i
        interior_coords += e
    return exterior_coords, interior_coords

# Define a function that saves outer and inner points to a .pt file
def _export_to_pt_file(self, file_name=r'C:\WizardTemp\test.pt'):
    '''create a .pt file in the format that pleases thinkdesign'''
    e, i = self.coords_ext_int()
    with open(file_name, 'w') as f:
        for rings in (e, i):
            for ring in rings:
                for x, y in ring:
                    f.write('{} {} 0\n'.format(x, y))

# Add the functions to the definition of the classes
# by assigning the functions to new class members
shapely.geometry.Polygon.coords_ext_int = _coords_ext_int_polygon
shapely.geometry.Polygon.export_to_pt_file = _export_to_pt_file

shapely.geometry.MultiPolygon.coords_ext_int = _coords_ext_int_multi_polygon
shapely.geometry.MultiPolygon.export_to_pt_file = _export_to_pt_file

Notice that the same function definition can be assigned to two different classes.

EDIT

In my example I'm not adding methods to a class of mine, I'm adding methods to shapely, an open source library I installed.

In your post you use p.get_name = ... to add a member to the object instance p. I first define a funciton _xxx(), then I add it to the class definition with class.xxx = _xxx.

I don't know your use case, but usually you add variables to instances and you add methods to class definitions, that's why I am showing you how to add methods to the class definition instead of to the instance.

Shapely manages geometric objects and offers methods to calculate the area of the polygons, to add or subtract polygons to each other, and many other really cool things.

My problem is that I need some methods that shapely doesn't provide out of the box.

In my example I created my own method that returns the list of points of the outer profile and the list of points of the inner profiles. I made two methods, one for the Polygon class and one for the MultiPolygon class.

I also need a method to export all the points to a .pt file format. In this case I made only one method that works with both the Polygon and the MultiPolygon classes.

This code is inside a module called shapely_monkeypatch.py (see monkey patch). When the module is imported the functions with the name starting by _ are defined, then they are assigned to the existing classes with names without _. (It is a convention in Python to use _ for names of variables or functions intended for internal use only.)

Community
  • 1
  • 1
stenci
  • 8,290
  • 14
  • 64
  • 104
0

I shall be maligned, pilloried, and excoriated, but... here is one way I make a keymap for an alphabet of methods within __init__(self).

def __init__(this):
    for c in "abcdefghijklmnopqrstuvwxyz":
        this.keymap[ord(c)] = eval(f"this.{c}")

Now, with appropriate code, I can press a key in pygame to execute the mapped method. It is easy enough to use lambdas so one does not even need pre-existing methods... for instance, if __str__(this) is a method, capital P can print the instance string representation using this code:

this.keymap[ord('P')] = lambda: print(this)

but everyone will tell you that eval is bad. I live to break rules and color outside the boundaries.

jlettvin
  • 1,113
  • 7
  • 13