For a long time I have been puzzled by Alex Martelli's remark about:
(...) the fuzzy unattainable goal of making repr's returned value
acceptable as input to eval!
So I gave it a try and came up with this:
class Sic():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
self.method_var = lambda x, y, z : x + y + z
def __repr__(self):
def right_quotes(value):
return repr(value).translate(str.maketrans('\'\"', '\"\''))
from inspect import signature
class_sig = signature(self.__class__)
fields = tuple('{}={}'.format(k,right_quotes(v)) for k,v in self.__dict__.items() if k in class_sig.parameters)
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'","")
Is this a correct general implementation of __repr__? If not could you give an example where it fails?
(I have improved the original version with the suggestion of Barmar, and responding to the objection of Kuco 23. I am looking here to a most general solution, even if it involves using introspection.)
What the quote means is that, when a string returned from the __repr__ method is ran on a python interpreter, it should evaluate to the object at its initialization stage.
The code you provided has a couple of faults.
Any object encoded in the __repr__ return string, should also be represented with their __repr__ method.
And also the self.__dict__.items() will return (name, value) pair for every attribute name set to the object self. The problem here is that some of those object were not used for the self's initialization. For example if your code was modified as
class Sic():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
self.method_var = someFunction(x, y, z)
def __repr__(self):
fields = tuple("{}={}".format(k, v) for k, v in self.__dict__.items())
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'","")
the repr method would return Sic(x=x0, y=y0, z=z0, method_var=mv0), even though that string's evaluation would be invalid, as the __init__ method only takes 3 arguments.
The safest option would be to implement the __repr__ method for any class you implement separately, as in
class Sic():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __repr__(self):
args = map(repr, (self.x, self.y, self.z))
return f"Sic({', '.join(args)})"
If you insist on defining a __repr__ method for a custom class, you would have to know for each object, which arguments the __init__ method takes in, which would probably require some additional modifications to every class and make the code more complex.
Related
I want to get class instance into function as argument, like code below. I know it doesn`t work.
class coordinate:
x = 1
y = 2
inst = coordinate()
def get_class(instance):
return instance.x + instance.y
get_class(inst)
Just getting instance`s inner variable is one of the solution, but the class that I want to use contains pretty diverse things. It makes code confusing
x = inst.x
y = inst.y
def get_class(x, y):
return x + y
get_class(x, y)
Is there any possible way?
First, let me point out that you probably want your x and y to be instance attributes and not class attributes.
Then if you want a function that takes a class instance as argument, what you want is probably an instance method.
class Coordinates:
def __init__(self, x, y):
self.x = x
self.y = y
def sum_coordinates(self):
return self.x + self.y
Coordinates(1, 2).sum_coordinates() # 3
This question already has answers here:
Calling a function of a module by using its name (a string)
(18 answers)
Closed 7 years ago.
I'm trying to assign class methods to class attribute, so I can call the methods from string. When using the class I want to call it from string like:
A.MAP['add'](x, y)
A.MAP['subtract'](x, y)
This is my current code:
class A:
MAP = {
'add' : A.add(x, y),
'subtract' : A.subtract(x, y),
}
#classmethod
def add(cls, x, y)
return x + y
#classmethod
def subtract(cls, x, y)
return x - y
However the result shown error that A is not defined at the line of assigning A.add to MAP['add']. For short functions I can use lambda. However, in case of a longer function, how can I achieve this design?
Note that when you try:
class A:
MAP = {
'add' : A.add(x, y),
'subtract' : A.subtract(x, y),
}
you are trying to access e.g. A.add before the name A exists (the class isn't bound to the name until definition completes) and before the name add exists (you haven't defined that method yet). Everything at the top level of the class definition is done in order.
You need to put the class methods into the dictionary after the class has been defined (they don't become callable until definition is complete):
class A:
MAP = {}
#classmethod
def add(cls, x, y): # note colon
return x + y
#classmethod
def subtract(cls, x, y): # also here
return x - y
A.MAP['add'] = A.add
A.MAP['subtract'] = A.subtract
Note that, as neither class method uses cls, you could make them #staticmethods instead. Or just use functions - Python isn't Java, you don't need to put everything into a class.
Alternatively, you can use getattr to access attributes (including class methods) by name:
>>> class A:
#classmethod
def add(cls, x, y):
return x + y
#classmethod
def subtract(cls, x, y):
return x - y
>>> getattr(A, 'add')(1, 2)
3
Please do not program in python like that, instead use a more standard oop approach like this:
#!/usr/bin/env python
class A:
def __init__(self):
pass
#classmethod
def add(self, x, y):
return x + y
#classmethod
def subtract(self, x, y):
return x - y
if __name__ == "__main__":
a = A()
print a.add(1,2) # ans: 3
print a.subtract(2,1) # ans: 1
When designing classes, I found it awkward to place default argument values in the __init__ method, as in:
class Class1(object):
def __init__(self, y=2, z=3):
self.y = self.manip_y(y)
self.z = self.manip_z(z)
def manip_y(self, y):
return y * 10
def manip_z(self, z):
return z - 30
Is it considered better practice to add **kwargs to function signatures to place default values in the function signatures as well?:
class Class2(object):
def __init__(self, **kwargs):
self.y = self.manip_y(**kwargs)
self.z = self.manip_z(**kwargs)
def manip_y(self, y=2, **kwargs):
return y * 10
def manip_z(self, z=3, **kwargs):
return z - 30
It's better to add default values in the __init__ signature -- that way someone only needs to look at the signature to figure out the options. And, in example 2, the default values are now hidden in other functions. Additionally, your documentation will be simpler.
do not do this. why? because it forces you to read not only the __init__ code to understand how to create the object but also all of the functions called therein.
Recently I've been trying to figure out a solution to the 'expression problem' of choosing between implementing my code in OOP or FP (functional programming). The example I used to illustrate my problem was a Vector2D class. I could make a class that contains all the necessary functions for a 2D vector (dot product, magnitude, etc.), or I could make a set of functions that take a 2-tuple representing a vector. Which option do I chose?
To cope with this problem, I thought it might be nice to make a decorator that takes a class's methods and turns them into global functions. This is how I did it:
import types
def function(method):
method._function = True
return method
def make_functions(cls):
for key in cls.__dict__:
method = getattr(cls, key)
if not isinstance(method, types.FunctionType):
continue
if hasattr(method, '_function') and method._function:
globals()[method.__name__] = method
return cls
#make_functions
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%g, %g)' % (self.x, self.y)
def __iter__(self):
for component in self.x, self.y:
yield component
def __getitem__(self, key):
return (self.x, self.y)[key]
def __setitem__(self, key, val):
if key == 0:
self.x = val
elif key == 1:
self.y = val
else:
print('not cool man')
def __add__(self, other):
x = self[0] + other[0]
y = self[1] + other[1]
return self.__class__(x, y)
__radd__ = __add__
def __sub__(self, other):
x = self[0] - other[0]
y = self[1] - other[1]
return self.__class__(x, y)
def __rsub__(self, other):
x = other[0] - self[0]
y = other[1] - self[1]
return self.__class__(x, y)
def __mul__(self, other):
x = other * self[0]
y = other * self[1]
return self.__class__(x, y)
__rmul__ = __mul__
#function
def dot_product(self, other):
return self[0]*other[1] + self[1]*other[0]
Now, dot_product is not only a method of the Vector2D class, but it is also a global function that takes in two vectors (or vector-like objects). This satisfies both the functional and object-oriented approaches to implementing an object like this. The only problem I can foresee this approach making is that any class that can be represented as another object like a tuple or a list, must be defined to work in the same ways as the objects which act like it. This is not so bad for a Vector that can also be a tuple, since all we have to do is define the __getitem__ and __iter__ methods, however I can see this getting wildly out of control for classes that have multiple contrasting implementations
Is this a fair solution to the problem? Is it bad practice or technique? Should I solely provide one or the other?
Python has a #staticmethod decorator for using class methods without an instantiation of that class. Simply annotate a class method with the static method wrapper (note the method now does not take a self reference), and you can call it from the class itself.
In your case, for the dot product, simply do:
class Vector2D():
# Magic methods here...
#staticmethod
def dot_product(a, b):
return a[0]*b[1] + a[1]*b[0]
Then, simply call Vector2D.dot_product(my_vector1, my_vector2) to use the function from the Vector2D class itself.
Assigning class methods to global functions sounds like a very dangerous, buggy, complex, and verbose solution. I would avoid it at all costs.
I am new in Python and in OOP in general. I have an error "...instance has no attribute '__getitem__'", and I understand that the object I have created is not a list. How can I make to be a list object. Here is the class file:
#!/usr/bin/python -tt
import math, sys, matrix, os
class Point:
'Class for points'
pointCount = 0
def __init__(self, x, y, z):
'initialise the Point from three coordinates'
self.x = x
self.y = y
self.z = z
Point.pointCount += 1
def __str__(self):
'print the Point'
return 'Point (%f, %f, %f)' %(self.x, self.y, self.z)
def copyPoint(self, distance):
'create another Point at distance from the self Point'
return Point(self.x + distance[0], self.y + distance[1], self.z + distance[2])
def __del__(self):
'delete the Point'
Point.pointCount -= 1
#print Point.pointCount
return '%s deleted' %self
I need to have it as a point with three coordinates inside (x, y, z), and those coordinates must be "callable" like in a list instance with [].
I have read similar topics but did not understand much. Please describe it in simple words and with examples.
Write a __getitem__ method:
def __getitem__(self, item):
return (self.x, self.y, self.z)[item]
This constructs a tuple of x, y, and z, and uses Python's own indexing facilities to access it.
Alternatively you could switch your own internal storage to be a tuple, and create properties for x, y and z:
def __init__(self, x, y, z):
self.coords = (x, y, z)
#property
def x(self): # sim. for y, z
return self.coords[0]
def __getitem__(self, item):
return self.coords[item]
I suggest you consider making your Point class using the collections.namedtuple factory function which will make it a subclass of the the built-in tuple class. This will save you some boiler-plate work. namedtuple class have attributes that can be accessed both by name, such as p.x and indexed, like p[0].
They are also very memory efficient like tuples, which may be important if you're going to have a lot of class instances.
You can further specialize what is returned by subclassing it, or use the verbose option to capture the source code and modify that as necessary.
There's an example in the documentation linked to above showing it being used to create a 2D Point class, which seems like it could be very helpful in your specific use-case.
Here's an example showing how one could define a custom 3D Point class via subclassing:
from collections import namedtuple
class Point(namedtuple('Point', 'x y z')):
__slots__ = () # prevent creation of instance dictionaries to save memory
point_count = 0 # instance counter
def __init__(self, *args):
super(Point, self).__init__(*args)
Point.point_count += 1
def distance(self, other):
return sum((self[i]-other[i])**2 for i in xrange(len(self))) ** 0.5
def copy_point(self, distance):
'create another Point at distance from the self Point'
return Point(*[dimension+distance for dimension in self])
p1 = Point(0, 0, 0)
print 'p1:', p1
p2 = p1.copy_point(20)
print 'p2: Point(%s)' % ', '.join(str(p2[i]) for i in xrange(len(p2)))
print 'distance p1 <-> p2: %.3f' % p1.distance(p2)
Output:
p1: Point(x=1, y=2, z=3)
p2: Point(21, 22, 23)
distance p1 <-> p2: 34.641
Note that by using namedtuple you don't have to implement a __getitem__() yourself, nor write a __str__() method. The only reason an __init__() was needed was because of the need to increment the class instance counter which was added -- something that namedtuples don't have or do by default.
Yes, you need to define __getitem__, but I would probably design the class as follows, which allows attribute and index access to the co-ordinates.
from collections import namedtuple
class Point(object):
def __init__(self, x, y, z):
self._coords = namedtuple('Coords', 'x y z')._make( (x, y, z) )
#property
def coords(self):
return self._coords
def __getitem__(self, item):
return self.coords[item]
def copy_point(self, distance):
return Point(*(el + distance for el in self.coords))
def __repr__(self):
return 'Point: {}'.format(self.coords)
p = Point(1, 2, 3)
p.copy_point(20), p.coords[0], p.coords.x
# (Point: Coords(x=21, y=22, z=23), 1, 1)