Python number-like class that remembers arithmetic operations? - python

I'm wondering if there exists a python module that would allow me to do something like this:
x = MagicNumber()
x.value = 3
y = 2 * (x + 2) ** 2 - 8
print y # 42
x.value = 2
print y # 24
So MagicNumber would implement all the special operator methods, and they would all return instances of MagicNumber, while keeping track of what operations are performed. Is there such a class?
EDIT: clarification
I want to use this in a module that should remember a lot of parameters of some arbitrary calculation that the user wishes to perform. So the user will set the parameters and then use them to produce his result. Then if he decides he'd like to alter a parameter, the change is reflected in his result immediately. So a very simplified usage session with only one parameter instance would look like:
p = MyParams()
p.distance = 13.4 # I use __getattr__ and __setattr__ such that
p.speed = 3.14 # __getattr__ returns MagicNumber instances
time = p.distance / p.speed
EDIT 2: more clarification
Okay, I'll do what I should have done from the start. I'll provide context.
You are an engineer and you're to produce a LaTeX document detailing the workings and properties of some prototype gadget. It is a task you'll do repeatedly for different prototypes. You write a small LaTeX python interface. For each prototype you create a python module that generates the requisite document. In it you type out the LaTeX code while calculating variables as they are needed, so that the calculations are in context. After a while you notice two problems:
The number of variables and parameters makes locals a mess and the variable names are hard to remember. You should group them into categories to keep track of them all.
You sometimes need to redo the same calculation, which is spread over the last couple of chapters and a dozen lines, with one or more parameters changed. You should find some way to avoid code duplication.
Out of this problem comes the original question.

Something like this?
import operator
MAKE_BINARY = lambda opfn : lambda self,other : BinaryOp(self, asMagicNumber(other), opfn)
MAKE_RBINARY = lambda opfn : lambda self,other : BinaryOp(asMagicNumber(other), self, opfn)
class MagicNumber(object):
__add__ = MAKE_BINARY(operator.add)
__sub__ = MAKE_BINARY(operator.sub)
__mul__ = MAKE_BINARY(operator.mul)
__radd__ = MAKE_RBINARY(operator.add)
__rsub__ = MAKE_RBINARY(operator.sub)
__rmul__ = MAKE_RBINARY(operator.mul)
# __div__ = MAKE_BINARY(operator.div)
# __rdiv__ = MAKE_RBINARY(operator.div)
__truediv__ = MAKE_BINARY(operator.truediv)
__rtruediv__ = MAKE_RBINARY(operator.truediv)
__floordiv__ = MAKE_BINARY(operator.floordiv)
__rfloordiv__ = MAKE_RBINARY(operator.floordiv)
def __neg__(self, other):
return UnaryOp(self, lambda x : -x)
#property
def value(self):
return self.eval()
class Constant(MagicNumber):
def __init__(self, value):
self.value_ = value
def eval(self):
return self.value_
class Parameter(Constant):
def __init__(self):
super(Parameter, self).__init__(0.0)
def setValue(self, v):
self.value_ = v
value = property(fset=setValue, fget=lambda self: self.value_)
class BinaryOp(MagicNumber):
def __init__(self, op1, op2, operation):
self.op1 = op1
self.op2 = op2
self.opn = operation
def eval(self):
return self.opn(self.op1.eval(), self.op2.eval())
class UnaryOp(MagicNumber):
def __init__(self, op1, operation):
self.op1 = op1
self.operation = operation
def eval(self):
return self.opn(self.op1.eval())
asMagicNumber = lambda x : x if isinstance(x, MagicNumber) else Constant(x)
And here it is in action:
x = Parameter()
# integer division
y = 2*x*x + 3*x - x//2
# or floating division
# y = 2*x*x + 3*x - x/2
x.value = 10
print(y.value)
# prints 225
x.value = 20
print(y.value)
# prints 850
# compute a series of x-y values for the function
print([(x.value, y.value) for x.value in range(5)])
# prints [(0, 0), (1, 5), (2, 13), (3, 26), (4, 42)]

You could give sympy, a computer algebra system written in Python, give a try.
E.g.
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> y = 2 * (x + 2) ** 2 - 8
>>> y
2*(x + 2)**2 - 8
>>> y.subs(x,3)
42
>>> y.subs(x,2)
24

Isn't this called a function? This may sound like a simple answer, but I mean it sincerely.
def y(x):
return 2 * (x + 2) ** 2 - 8
Aren't you thinking in the wrong direction with this one?
To address the clarification:
class MyParams():
distance = 0.0
speed = 0.0
def __call__(self):
return self.distance / self.speed
p = MyParams()
p.distance = 13.4 # These are properties
p.speed = 3.14 # where __get__ returns MagicNumber instances
time = p() # 4.26
p.speed = 2.28
time = p() # 5.88
I guess I'm more in favor of this type of a solution, although I see the benefit in the sympy module. Preference, I guess.

>>> magic = lambda x: eval('2 * (x + 2) ** 2 - 8')
>>> magic(2)
24
>>> magic(3)
42
>>> magic = lambda x: eval('x ** 4')
>>> magic(2)
16
>>> magic(3)
81

I think the difficulty is in "how to keep operators priority" rather than implementing a class.
I suggest to look at a different notation (like Reverse Polish Notation) that may help in getting rid of priority issues...

Related

how to extend the method of a class in a child class without returning all its variables

Let's say I have a base class:
class B:
def __init__(self):
self.b = 3
return
def method(self,x):
y = 2 * self.b
x *= 2
y *= (x + 2)
return x
It does some manipulations based on self.b and produces a variable y that is not returned by method.
Say, I have now a class A which derives from B and will look very similar, but it shall extend the functionality of B's method() by doing additional operations:
class A(B)
def __init__(self):
super().__init__()
return
def extended_method(self,x):
x = self.method(x)
if y<=10:
y = 10
return x,y
def what_As_extended_method_should_do(self,x):
y = 2 * self.b
x *= 2
y *= (x + 2)
if y<=10:
y = 10
return x,y
extended_method shows the way I'd like to write it, but this will cause an undefined variable error. The method what_As_extended_method_should_do shows what the method should do. But instead of having to write the same code inside B.method() several times (say I have 10 different subclasses which all slightly change B.method(), I would like to have some wrapper that automatically takes B.method()'s y into its scope and can operate on it without undefined variable error. The crux is that I cannot simply return x AND y from B.method(), just x. Does anybody know a solution?
Thanks!

print depending how many arguments given

Python beginner here trying to learn by doing, here i have two functions 'main','area'.
the one which has two arguments should print y = math.pi *a*b and the one which does not have two arguments should print x = math.pi *a**2 in my code it is currently printing like this " First (153.93804002589985, 0.0)
Second (78.53981633974483, 62.83185307179586) " (why its printing these 0.0 and 78.53981633974483?), how to make it check that if one parameter is given do this and if two do that ?
import math
def area(a,b=0):
y = math.pi *a*b
x = math.pi *a**2
return x,y
def main():
print("First", area(7))
print("Second", area(5, 4))
main()
If I understand you correctly, you want something like this:
import math
def area(a, b=None):
if b is None:
# b not specified:
x = math.pi * a**2
else:
# b specified:
x = math.pi * a*b
return x
def main():
print("First", area(7))
print("Second", area(5, 4))
main()
Output is
First 153.93804002589985
Second 62.83185307179586
When b is not specified, it is set to None. Then you test for that in the function.
The reason it prints e.g. (153.93804002589985, 0.0) in your original example is that you return a tuple (x, y) from the function with return x,y.
Your function returns a tuple, but the caller of the functions does not know which value to use. Instead, your function should just return either one or the other value, depending on the provided inputs. Also, I would suggest using None as the default for b since 0 might be a valid value.
def area(a, b=None):
if b is None:
return math.pi * a**2
else:
return math.pi * a * b
Alternatively, you could also use a ternary ... if ... else ... for either the entire expression or just the part that is different:
def area(a, b=None):
return math.pi * a * (b if b is not None else a)
The output will then be just
First 153.93804002589985
Second 62.83185307179586

Existence of mutable named tuple in Python?

Can anyone amend namedtuple or provide an alternative class so that it works for mutable objects?
Primarily for readability, I would like something similar to namedtuple that does this:
from Camelot import namedgroup
Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
Point(x=100, y=0)
It must be possible to pickle the resulting object. And per the characteristics of named tuple, the ordering of the output when represented must match the order of the parameter list when constructing the object.
There is a mutable alternative to collections.namedtuple – recordclass.
It can be installed from PyPI:
pip3 install recordclass
It has the same API and memory footprint as namedtuple and it supports assignments (It should be faster as well). For example:
from recordclass import recordclass
Point = recordclass('Point', 'x y')
>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)
recordclass (since 0.5) support typehints:
from recordclass import recordclass, RecordClass
class Point(RecordClass):
x: int
y: int
>>> Point.__annotations__
{'x':int, 'y':int}
>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)
There is a more complete example (it also includes performance comparisons).
Recordclass library now provides another variant -- recordclass.make_dataclass factory function. It support dataclasses-like API (there are module level functions update, make, replace instead of self._update, self._replace, self._asdict, cls._make methods).
from recordclass import dataobject, make_dataclass
Point = make_dataclass('Point', [('x', int), ('y',int)])
Point = make_dataclass('Point', {'x':int, 'y':int})
class Point(dataobject):
x: int
y: int
>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> p.x = 10; p.y += 3; print(p)
Point(x=10, y=5)
recordclass and make_dataclass can produce classes, whose instances occupy less memory than __slots__-based instances. This can be important for the instances with attribute values, which has not intended to have reference cycles. It may help reduce memory usage if you need to create millions of instances. Here is an illustrative example.
types.SimpleNamespace was introduced in Python 3.3 and supports the requested requirements.
from types import SimpleNamespace
t = SimpleNamespace(foo='bar')
t.ham = 'spam'
print(t)
namespace(foo='bar', ham='spam')
print(t.foo)
'bar'
import pickle
with open('/tmp/pickle', 'wb') as f:
pickle.dump(t, f)
As a Pythonic alternative for this task, since Python-3.7, you can use
dataclasses module that not only behaves like a mutable NamedTuple, because they use normal class definitions, they also support other class features.
From PEP-0557:
Although they use a very different mechanism, Data Classes can be thought of as "mutable namedtuples with defaults". Because Data Classes use normal class definition syntax, you are free to use inheritance, metaclasses, docstrings, user-defined methods, class factories, and other Python class features.
A class decorator is provided which inspects a class definition for variables with type annotations as defined in PEP 526, "Syntax for Variable Annotations". In this document, such variables are called fields. Using these fields, the decorator adds generated method definitions to the class to support instance initialization, a repr, comparison methods, and optionally other methods as described in the Specification section. Such a class is called a Data Class, but there's really nothing special about the class: the decorator adds generated methods to the class and returns the same class it was given.
This feature is introduced in PEP-0557 that you can read about it in more details on provided documentation link.
Example:
In [20]: from dataclasses import dataclass
In [21]: #dataclass
...: class InventoryItem:
...: '''Class for keeping track of an item in inventory.'''
...: name: str
...: unit_price: float
...: quantity_on_hand: int = 0
...:
...: def total_cost(self) -> float:
...: return self.unit_price * self.quantity_on_hand
...:
Demo:
In [23]: II = InventoryItem('bisc', 2000)
In [24]: II
Out[24]: InventoryItem(name='bisc', unit_price=2000, quantity_on_hand=0)
In [25]: II.name = 'choco'
In [26]: II.name
Out[26]: 'choco'
In [27]:
In [27]: II.unit_price *= 3
In [28]: II.unit_price
Out[28]: 6000
In [29]: II
Out[29]: InventoryItem(name='choco', unit_price=6000, quantity_on_hand=0)
The latest namedlist 1.7 passes all of your tests with both Python 2.7 and Python 3.5 as of Jan 11, 2016. It is a pure python implementation whereas the recordclass is a C extension. Of course, it depends on your requirements whether a C extension is preferred or not.
Your tests (but also see the note below):
from __future__ import print_function
import pickle
import sys
from namedlist import namedlist
Point = namedlist('Point', 'x y')
p = Point(x=1, y=2)
print('1. Mutation of field values')
p.x *= 10
p.y += 10
print('p: {}, {}\n'.format(p.x, p.y))
print('2. String')
print('p: {}\n'.format(p))
print('3. Representation')
print(repr(p), '\n')
print('4. Sizeof')
print('size of p:', sys.getsizeof(p), '\n')
print('5. Access by name of field')
print('p: {}, {}\n'.format(p.x, p.y))
print('6. Access by index')
print('p: {}, {}\n'.format(p[0], p[1]))
print('7. Iterative unpacking')
x, y = p
print('p: {}, {}\n'.format(x, y))
print('8. Iteration')
print('p: {}\n'.format([v for v in p]))
print('9. Ordered Dict')
print('p: {}\n'.format(p._asdict()))
print('10. Inplace replacement (update?)')
p._update(x=100, y=200)
print('p: {}\n'.format(p))
print('11. Pickle and Unpickle')
pickled = pickle.dumps(p)
unpickled = pickle.loads(pickled)
assert p == unpickled
print('Pickled successfully\n')
print('12. Fields\n')
print('p: {}\n'.format(p._fields))
print('13. Slots')
print('p: {}\n'.format(p.__slots__))
Output on Python 2.7
1. Mutation of field values
p: 10, 12
2. String
p: Point(x=10, y=12)
3. Representation
Point(x=10, y=12)
4. Sizeof
size of p: 64
5. Access by name of field
p: 10, 12
6. Access by index
p: 10, 12
7. Iterative unpacking
p: 10, 12
8. Iteration
p: [10, 12]
9. Ordered Dict
p: OrderedDict([('x', 10), ('y', 12)])
10. Inplace replacement (update?)
p: Point(x=100, y=200)
11. Pickle and Unpickle
Pickled successfully
12. Fields
p: ('x', 'y')
13. Slots
p: ('x', 'y')
The only difference with Python 3.5 is that the namedlist has become smaller, the size is 56 (Python 2.7 reports 64).
Note that I have changed your test 10 for in-place replacement. The namedlist has a _replace() method which does a shallow copy, and that makes perfect sense to me because the namedtuple in the standard library behaves the same way. Changing the semantics of the _replace() method would be confusing. In my opinion the _update() method should be used for in-place updates. Or maybe I failed to understand the intent of your test 10?
It seems like the answer to this question is no.
Below is pretty close, but it's not technically mutable. This is creating a new namedtuple() instance with an updated x value:
Point = namedtuple('Point', ['x', 'y'])
p = Point(0, 0)
p = p._replace(x=10)
On the other hand, you can create a simple class using __slots__ that should work well for frequently updating class instance attributes:
class Point:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
To add to this answer, I think __slots__ is good use here because it's memory efficient when you create lots of class instances. The only downside is that you can't create new class attributes.
Here's one relevant thread that illustrates the memory efficiency - Dictionary vs Object - which is more efficient and why?
The quoted content in the answer of this thread is a very succinct explanation why __slots__ is more memory efficient - Python slots
The following is a good solution for Python 3: A minimal class using __slots__ and Sequence abstract base class; does not do fancy error detection or such, but it works, and behaves mostly like a mutable tuple (except for typecheck).
from collections import Sequence
class NamedMutableSequence(Sequence):
__slots__ = ()
def __init__(self, *a, **kw):
slots = self.__slots__
for k in slots:
setattr(self, k, kw.get(k))
if a:
for k, v in zip(slots, a):
setattr(self, k, v)
def __str__(self):
clsname = self.__class__.__name__
values = ', '.join('%s=%r' % (k, getattr(self, k))
for k in self.__slots__)
return '%s(%s)' % (clsname, values)
__repr__ = __str__
def __getitem__(self, item):
return getattr(self, self.__slots__[item])
def __setitem__(self, item, value):
return setattr(self, self.__slots__[item], value)
def __len__(self):
return len(self.__slots__)
class Point(NamedMutableSequence):
__slots__ = ('x', 'y')
Example:
>>> p = Point(0, 0)
>>> p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
>>> p
Point(x=100, y=0)
If you want, you can have a method to create the class too (though using an explicit class is more transparent):
def namedgroup(name, members):
if isinstance(members, str):
members = members.split()
members = tuple(members)
return type(name, (NamedMutableSequence,), {'__slots__': members})
Example:
>>> Point = namedgroup('Point', ['x', 'y'])
>>> Point(6, 42)
Point(x=6, y=42)
In Python 2 you need to adjust it slightly - if you inherit from Sequence, the class will have a __dict__ and the __slots__ will stop from working.
The solution in Python 2 is to not inherit from Sequence, but object. If isinstance(Point, Sequence) == True is desired, you need to register the NamedMutableSequence as a base class to Sequence:
Sequence.register(NamedMutableSequence)
Tuples are by definition immutable.
You can however make a dictionary subclass where you can access the attributes with dot-notation;
In [1]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class AttrDict(dict):
:
: def __getattr__(self, name):
: return self[name]
:
: def __setattr__(self, name, value):
: self[name] = value
:--
In [2]: test = AttrDict()
In [3]: test.a = 1
In [4]: test.b = True
In [5]: test
Out[5]: {'a': 1, 'b': True}
If you want similar behavior as namedtuples but mutable try namedlist
Note that in order to be mutable it cannot be a tuple.
Let's implement this with dynamic type creation:
import copy
def namedgroup(typename, fieldnames):
def init(self, **kwargs):
attrs = {k: None for k in self._attrs_}
for k in kwargs:
if k in self._attrs_:
attrs[k] = kwargs[k]
else:
raise AttributeError('Invalid Field')
self.__dict__.update(attrs)
def getattribute(self, attr):
if attr.startswith("_") or attr in self._attrs_:
return object.__getattribute__(self, attr)
else:
raise AttributeError('Invalid Field')
def setattr(self, attr, value):
if attr in self._attrs_:
object.__setattr__(self, attr, value)
else:
raise AttributeError('Invalid Field')
def rep(self):
d = ["{}={}".format(v,self.__dict__[v]) for v in self._attrs_]
return self._typename_ + '(' + ', '.join(d) + ')'
def iterate(self):
for x in self._attrs_:
yield self.__dict__[x]
raise StopIteration()
def setitem(self, *args, **kwargs):
return self.__dict__.__setitem__(*args, **kwargs)
def getitem(self, *args, **kwargs):
return self.__dict__.__getitem__(*args, **kwargs)
attrs = {"__init__": init,
"__setattr__": setattr,
"__getattribute__": getattribute,
"_attrs_": copy.deepcopy(fieldnames),
"_typename_": str(typename),
"__str__": rep,
"__repr__": rep,
"__len__": lambda self: len(fieldnames),
"__iter__": iterate,
"__setitem__": setitem,
"__getitem__": getitem,
}
return type(typename, (object,), attrs)
This checks the attributes to see if they are valid before allowing the operation to continue.
So is this pickleable? Yes if (and only if) you do the following:
>>> import pickle
>>> Point = namedgroup("Point", ["x", "y"])
>>> p = Point(x=100, y=200)
>>> p2 = pickle.loads(pickle.dumps(p))
>>> p2.x
100
>>> p2.y
200
>>> id(p) != id(p2)
True
The definition has to be in your namespace, and must exist long enough for pickle to find it. So if you define this to be in your package, it should work.
Point = namedgroup("Point", ["x", "y"])
Pickle will fail if you do the following, or make the definition temporary (goes out of scope when the function ends, say):
some_point = namedgroup("Point", ["x", "y"])
And yes, it does preserve the order of the fields listed in the type creation.
I can't believe nobody's said this before, but it seems to me Python just wants you to write your own simple, mutable class instead of using a namedtuple whenever you need the "namedtuple" to be mutable.
Quick summary
Just jump straight down to Approach 5 below. It's short and to-the-point, and by far the best of these options.
Various, detailed approaches:
Approach 1 (good): simple, callable class with __call__()
Here is an example of a simple Point object for (x, y) points:
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self):
"""
Make `Point` objects callable. Print their contents when they
are called.
"""
print("Point(x={}, y={})".format(self.x, self.y))
Now use it:
p1 = Point(1,2)
p1()
p1.x = 7
p1()
p1.y = 8
p1()
Here is the output:
Point(x=1, y=2)
Point(x=7, y=2)
Point(x=7, y=8)
This is pretty similar to a namedtuple, except it is fully mutable, unlike a namedtuple. Also, a namedtuple isn't callable, so to see its contents, just type the object instance name withOUT parenthesis after it (as p2 in the example below, instead of as p2()). See this example and output here:
>>> from collections import namedtuple
>>> Point2 = namedtuple("Point2", ["x", "y"])
>>> p2 = Point2(1, 2)
>>> p2
Point2(x=1, y=2)
>>> p2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Point2' object is not callable
>>> p2.x = 7
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Approach 2 (better): use __repr__() in place of __call__()
I just learned you can use __repr__() in place of __call__(), to get more namedtuple-like behavior. Defining the __repr__() method allows you to define "the 'official' string representation of an object" (see the official documentation here). Now, just calling p1 is the equivalent of calling the __repr__() method, and you get identical behavior to the namedtuple. Here is the new class:
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
"""
Obtain the string representation of `Point`, so that just typing
the instance name of an object of this type will call this method
and obtain this string, just like `namedtuple` already does!
"""
return "Point(x={}, y={})".format(self.x, self.y)
Now use it:
p1 = Point(1,2)
p1
p1.x = 7
p1
p1.y = 8
p1
Here is the output:
Point(x=1, y=2)
Point(x=7, y=2)
Point(x=7, y=8)
Approach 3 (better still, but a little awkward to use): make it a callable which returns an (x, y) tuple
The original poster (OP) would also like something like this to work (see his comment below my answer):
x, y = Point(x=1, y=2)
Well, for simplicity, let's just make this work instead:
x, y = Point(x=1, y=2)()
# OR
p1 = Point(x=1, y=2)
x, y = p1()
While we are at it, let's also condense this:
self.x = x
self.y = y
...into this (source where I first saw this):
self.x, self.y = x, y
Here is the class definition for all of the above:
class Point():
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
"""
Obtain the string representation of `Point`, so that just typing
the instance name of an object of this type will call this method
and obtain this string, just like `namedtuple` already does!
"""
return "Point(x={}, y={})".format(self.x, self.y)
def __call__(self):
"""
Make the object callable. Return a tuple of the x and y components
of the Point.
"""
return self.x, self.y
Here are some test calls:
p1 = Point(1,2)
p1
p1.x = 7
x, y = p1()
x2, y2 = Point(10, 12)()
x
y
x2
y2
I won't show pasting the class definition into the interpreter this time, but here are those calls with their output:
>>> p1 = Point(1,2)
>>> p1
Point(x=1, y=2)
>>> p1.x = 7
>>> x, y = p1()
>>> x2, y2 = Point(10, 12)()
>>> x
7
>>> y
2
>>> x2
10
>>> y2
12
Approach 4 (best so far, but a lot more code to write): make the class also an iterator
By making this into an iterator class, we can get this behavior:
x, y = Point(x=1, y=2)
# OR
x, y = Point(1, 2)
# OR
p1 = Point(1, 2)
x, y = p1
Let's get rid of the __call__() method, but to make this class an iterator we will add the __iter__() and __next__() methods. Read more about these things here:
https://treyhunner.com/2018/06/how-to-make-an-iterator-in-python/
How to build a basic iterator?
https://docs.python.org/3/library/exceptions.html#StopIteration
Here is the solution:
class Point():
def __init__(self, x, y):
self.x, self.y = x, y
self._iterator_index = 0
self._num_items = 2 # counting self.x and self.y
def __repr__(self):
"""
Obtain the string representation of `Point`, so that just typing
the instance name of an object of this type will call this method
and obtain this string, just like `namedtuple` already does!
"""
return "Point(x={}, y={})".format(self.x, self.y)
def __iter__(self):
return self
def __next__(self):
self._iterator_index += 1
if self._iterator_index == 1:
return self.x
elif self._iterator_index == 2:
return self.y
else:
raise StopIteration
And here are some test calls and their output:
>>> x, y = Point(x=1, y=2)
>>> x
1
>>> y
2
>>> x, y = Point(3, 4)
>>> x
3
>>> y
4
>>> p1 = Point(5, 6)
>>> x, y = p1
>>> x
5
>>> y
6
>>> p1
Point(x=5, y=6)
Approach 5 (USE THIS ONE) (Perfect!--best and cleanest/shortest approach): make the class an iterable, with the yield generator keyword
Study these references:
https://treyhunner.com/2018/06/how-to-make-an-iterator-in-python/
What does the "yield" keyword do?
Here is the solution. It relies on a fancy "iterable-generator" (AKA: just "generator") keyword/Python mechanism, called yield.
Basically, the first time an iterable calls for the next item, it calls the __iter__() method, and stops and returns the contents of the first yield call (self.x in the code below). The next time an iterable calls for the next item, it picks up where it last left off (just after the first yield in this case), and looks for the next yield, stopping and returning the contents of that yield call (self.y in the code below). Each "return" from a yield actually returns a "generator" object, which is an iterable itself, so you can iterate on it. Each new iterable call for the next item continues this process, starting up where it last left off, just after the most-recently-called yield, until no more yield calls exist, at which point the iterations are ended and the iterable has been fully iterated. Therefore, once this iterable has called for two objects, both yield calls have been used up, so the iterator ends. The end result is that calls like this work perfectly, just as they did in Approach 4, but with far less code to write!:
x, y = Point(x=1, y=2)
# OR
x, y = Point(1, 2)
# OR
p1 = Point(1, 2)
x, y = p1
Here is the solution (a part of this solution can also be found in the treyhunner.com reference just above). Notice how short and clean this solution is!
Just the class definition code; no docstrings, so you can truly see how short and simple this is:
class Point():
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return "Point(x={}, y={})".format(self.x, self.y)
def __iter__(self):
yield self.x
yield self.y
With descriptive docstrings:
class Point():
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
"""
Obtain the string representation of `Point`, so that just typing
the instance name of an object of this type will call this method
and obtain this string, just like `namedtuple` already does!
"""
return "Point(x={}, y={})".format(self.x, self.y)
def __iter__(self):
"""
Make this `Point` class an iterable. When used as an iterable, it will
now return `self.x` and `self.y` as the two elements of a list-like,
iterable object, "generated" by the usages of the `yield` "generator"
keyword.
"""
yield self.x
yield self.y
Copy and paste the exact same test code as used in the previous approach (Approach 4) just above, and you will get the exact same output as above as well!
References:
https://docs.python.org/3/library/collections.html#collections.namedtuple
Approach 1:
What is the difference between __init__ and __call__?
Approach 2:
https://www.tutorialspoint.com/What-does-the-repr-function-do-in-Python-Object-Oriented-Programming
Purpose of __repr__ method?
https://docs.python.org/3/reference/datamodel.html#object.__repr__
Approach 4:
*****[EXCELLENT!] https://treyhunner.com/2018/06/how-to-make-an-iterator-in-python/
How to build a basic iterator?
https://docs.python.org/3/library/exceptions.html#StopIteration
Approach 5:
See links from Approach 4, plus:
*****[EXCELLENT!] What does the "yield" keyword do?
What is the meaning of single and double underscore before an object name?
Provided performance is of little importance, one could use a silly hack like:
from collection import namedtuple
Point = namedtuple('Point', 'x y z')
mutable_z = Point(1,2,[3])
If you want to be able to create classes "on-site", I find the following very convenient:
class Struct:
def __init__(self, **kw):
self.__dict__.update(**kw)
That allows me to write:
p = Struct(x=0, y=0)
P.x = 10
stats = Struct(count=0, total=0.0)
stats.count += 1
The most elegant way I can think of doesn't require a 3rd party library and lets you create a quick mock class constructor with default member variables without dataclasses cumbersome type specification. So it's better for roughing out some code:
# copy-paste 3 lines:
from inspect import getargvalues, stack
from types import SimpleNamespace
def DefaultableNS(): return SimpleNamespace(**getargvalues(stack()[1].frame)[3])
# then you can make classes with default fields on the fly in one line, eg:
def Node(value,left=None,right=None): return DefaultableNS()
node=Node(123)
print(node)
#[stdout] namespace(value=123, left=None, right=None)
print(node.value,node.left,node.right) # all fields exist
A plain SimpleNamespace is clumsier, it breaks DRY:
def Node(value,left=None,right=None):
return SimpleNamespace(value=value,left=left,right=right)
# breaks DRY as you need to repeat the argument names twice
I will share my solution to this question. I needed a way to save attributes in the case that my program crashed or was stopped for some reason so that it would know where where in a list of inputs to resume from. Based on #GabrielStaples's answer:
import pickle, json
class ScanSession:
def __init__(self, input_file: str = None, output_file: str = None,
total_viable_wallets: int = 0, total: float = 0,
report_dict: dict = {}, wallet_addresses: list = [],
token_map: list = [], token_map_file: str = 'data/token.maps.json',
current_batch: int = 0):
self.initialized = time.time()
self.input_file = input_file
self.output_file = output_file
self.total_viable_wallets = total_viable_wallets
self.total = total
self.report_dict = report_dict
self.wallet_addresses = wallet_addresses
self.token_map = token_map
self.token_map_file = token_map_file
self.current_batch = current_batch
#property
def __dict__(self):
"""
Obtain the string representation of `Point`, so that just typing
the instance name of an object of this type will call this method
and obtain this string, just like `namedtuple` already does!
"""
return {'initialized': self.initialized, 'input_file': self.input_file,
'output_file': self.output_file, 'total_viable_wallets': self.total_viable_wallets,
'total': self.total, 'report_dict': self.report_dict,
'wallet_addresses': self.wallet_addresses, 'token_map': self.token_map,
'token_map_file':self.token_map_file, 'current_batch': self.current_batch
}
def load_session(self, session_file):
with open(session_file, 'r') as f:
_session = json.loads(json.dumps(f.read()))
_session = dict(_session)
for key, value in _session.items():
setattr(self, key, value)
def dump_session(self, session_file):
with open(session_file, 'w') as f:
json.dump(self.__dict__, fp=f)
Using it:
session = ScanSession()
session.total += 1
session.__dict__
{'initialized': 1670801774.8050613, 'input_file': None, 'output_file': None, 'total_viable_wallets': 0, 'total': 10, 'report_dict': {}, 'wallet_addresses': [], 'token_map': [], 'token_map_file': 'data/token.maps.json', 'current_batch': 0}
pickle.dumps(session)
b'\x80\x04\x95\xe8\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x0bScanSession\x94\x93\x94)\x81\x94}\x94(\x8c\x0binitialized\x94GA\xd8\xe5\x9a[\xb3\x86 \x8c\ninput_file\x94N\x8c\x0boutput_file\x94N\x8c\x14total_viable_wallets\x94K\x00\x8c\x05total\x94K\n\x8c\x0breport_dict\x94}\x94\x8c\x10wallet_addresses\x94]\x94\x8c\ttoken_map\x94]\x94\x8c\x0etoken_map_file\x94\x8c\x14data/token.maps.json\x94\x8c\rcurrent_batch\x94K\x00ub.'

Function Calling With 3 or More Argument Input Fields - function()()()

I am studying the properties of functions in Python and I came across an exercise that asks to:
Write a function which returns de power of a number. Conditions: The function may only take 1 argument and must use another function to return the value of the power of a given number.
The code that solves this exercise is:
def power(x):
return lambda y: y**x
For example, if we would like to know the value of the power: 2^3, we would call the function like this: power(3)(2)
Here is what I would like to know:
Is there any way to write a function that, when called, has a similar structure: function()()().
In other words, is it possible to write a function, that requires three or more parentheses ()()() when called?
If it is possible, could you please give me an example code of that function and briefly explain it?
Also:
def power(x):
def power_extra(y):
return y
def power_another(z):
return z
return power_extra and power_another
Possible?
Sure you can:
def power_times(k):
"""use as power_times(k)(x)(y) => k * y^x"""
return lambda x: lambda y: k * y**x
print power_times(2)(3)(4) # returns 2 * 4^3 = 128
When you call this function with argument 2 (power_times(2)), it returns a lambda function that works like lambda x: lambda y: 2 * y ** x (that is, like your original function, only with an extra "times 2").
You can stack as many lambdas on top of each other as you like:
def many_lambdas(x):
"""many_lambdas(x)(y)(z)(q) => x + y * z^q"""
return lambda y: lambda z: lambda q: x + y * z ** q
print many_lambdas(1)(2)(3)(4) # prints 163
Indeed, it might be even clearer if you skipped using def at all, and just wrote:
many_lambdas = lambda x: lambda y: lambda z: lambda q: x + y * z ** q
Or, alternatively, you could skip using lambda ever and just use them as nested functions:
def many_funcs(x):
def many_funcs_y(y):
def many_funcs_z(z):
def many_funcs_q(q):
return x + y * z ** q
return many_funcs_q
return many_funcs_z
return many_funcs_y
print many_funcs(1)(2)(3)(4) # prints 163
#David's answer would aptly answer you question for fixed nested function calls. For undefined nesting, you may want to define a class and overload the __call__ method along with __repr__ and __int__ to serve your Purpose.
>>> class Power(object):
def __init__(self, value):
self.value = value
def __call__(self, value):
self.value **= value
return self
def __int__(self):
return self.value
def __repr__(self):
return str(self.value)
>>> print Power(2)(2)(2)(2)(2)
65536
>>> int(Power(2)(2)(2)(2)(2)) / 2
32768

__float__ and __round__ in python 2 and 3

One of the changes between python 2 and 3 is that the latter delegates to x.__round__([n]) the operation round(x, n). In python 2, for my classes implementing __round__ and __float__, when I call round(x), x.__float__ is called.
How can I know that round(x) (and not float(x)) was called to reroute the call appropriate in python 2 and obtain a python 3 like behaviour.
thanks
Update: I came up with an ugly hack. I am sure that:
it can be improved.
it will not always work.
The ndigits parameter is not handled in python 2.
it should not be used in production.
but it was interesting to build it anyway. Thanks for all the clarifications.
import dis
import sys
import inspect
import functools
#'CALL_FUNCTION', 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW'
HUNGRY = (131, 140, 141, 142)
if sys.version < '3':
def is_round(frame):
"""Disassemble a code object."""
co = frame.f_code
lasti = frame.f_lasti
code = co.co_code
i, n = 0, len(code)
extended_arg = 0
free = None
codes = list()
while i < n:
c = code[i]
op = ord(c)
tmp = [op, ]
i += 1
if op >= dis.HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg
extended_arg = 0
i += 2
if op == dis.EXTENDED_ARG:
extended_arg = oparg * long(65536)
tmp.append(oparg)
if op in dis.hasconst:
tmp.append(repr(co.co_consts[oparg]))
elif op in dis.hasname:
tmp.append(co.co_names[oparg])
elif op in dis.hasjrel:
tmp.append(repr(i + oparg)),
elif op in dis.haslocal:
tmp.append(co.co_varnames[oparg])
elif op in dis.hascompare:
tmp.append(dis.cmp_op[oparg])
elif op in dis.hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
tmp.append(free[oparg])
else:
tmp.append(None)
else:
tmp.append(None)
tmp.append(None)
codes.append(tmp)
if i > lasti:
break
pending = 1
for (opcode, arguments, param) in reversed(codes):
pending -= 1
if opcode in HUNGRY:
pending += arguments + 1
if not pending:
seen = dict(frame.f_builtins)
seen.update(frame.f_globals)
seen.update(frame.f_locals)
while param in seen:
param = seen[param]
return param == round
def round_check(func):
#functools.wraps(func)
def wrapped(self):
if is_round(inspect.currentframe().f_back):
return self.__round__()
return func(self)
return wrapped
else:
def round_check(func):
return func
class X():
#round_check
def __float__(self):
return 1.0
def __round__(self, ndigits=0):
return 2.0
x = X()
r = round
f = float
assert round(x) == 2.0
assert float(x) == 1.0
assert r(x) == 2.0
assert f(x) == 1.0
assert round(float(x)) == 1.0
assert float(round(x)) == 2.0
You could always redefine round to try __round__ first. Unfortunately this isn't a __future__ import, so I don't think there's much else you can do.
>>> class X(object):
... def __round__(self, n=0): return 1.
... def __float__(self): return 2.
...
>>> x = X()
>>> round(x)
2.0
>>> float(x)
2.0
>>> old_round = round
>>> def round(x, n=0):
... try:
... return x.__round__(n)
... except AttributeError:
... return old_round(x)
...
>>>
>>> round(x)
1.0
>>> float(x)
2.0
>>>
Note that this is at least a documented change:
The round() function rounding strategy and return type have changed.
Exact halfway cases are now rounded to the nearest even result instead
of away from zero. (For example, round(2.5) now returns 2 rather than
3.) round(x[, n])() now delegates to x.__round__([n]) instead of always returning a float. It generally returns an integer when called
with a single argument and a value of the same type as x when called
with two arguments.
In Python 2, you can not override what round() does. It does not delegate to __float__; it first calls float() (which in turn delegates to __float__), then does the rounding. There is therefore no point in knowing if __float__ is called from round() or not, as it will do the rounding for you. You can't delegate it.
If you want to implement your own custom rounding in Python 2, you should implement a custom_round() method that does the custom rounding, and use that instead of round().
How can I know that round(x) (and not float(x)) was called to reroute the call appropriate in python 2 and obtain a python 3 like behaviour.
You don't need to. if round(x) calls your __float__ method, it will round the returned floats using the normal logic for floats. You do not need to consider it in the __float__ implementation; you should return the same thing regardless of the calling context. Everything else is the calling context's responsibility.
>>> class hax(object):
... def __float__(self): return 2.6
...
>>> round(hax())
3.0

Categories