Python: defining my own operators?
No, you can't create new operators. However, if you are just evaluating expressions, you could process the string yourself and calculate the results of the new operators.
Is it possible to define a new mathematical operator in python?
You can use one of the predefined operator that has not been implemented for integers.
For example this one : @ (matmul)
You cannot use !
character as it is not in this list.
class MyInt(int):
def __matmul__(self, other):
return self / (self + other)
Then you can do :
a = MyInt(1)
b = MyInt(2)
print(a @ b)
Will print (0.33333333)
How do I define my own operators in TensorFlow
As the comment suggested, there is a how-to guide for adding an op to TensorFlow. This guide covers adding a new op that is implemented in C++. In general, you should do this in the following situations:
- The op cannot be implemented using existing TensorFlow ops (for example,
l1_loss
could be implemented using the existing element-wise and reduction operators as a Python function). - A C++ implementation is necessary for performance (or memory consumption) reasons.
- The op could be implemented as a composition of ops, but it has a gradient that can be computed more efficiently (or with better numerical stability) than computing the gradients op-by-op. (This is why
tf.nn.l2_loss
is implemented as a fused op in C++.)
New operators in Python
Expanding on @fasouto answer, but adding a bit more code.
While you cannot define new operators AND you cannot redefine existing operators for built-in types, what you can do is to define a class (instantiated to any valid Python name, e.g. op
) that act as an intermediate binding for two objects, thus effectively looking like a binary infix operator:
a | op | b
Non-binding Implementation
In short, one can define a class overriding forward and backward methods for an operator, e.g. __or__
and __ror__
for the |
operator:
class Infix:
def __init__(self, function):
self.function = function
def __ror__(self, other):
return Infix(lambda x, self=self, other=other: self.function(other, x))
def __or__(self, other):
return self.function(other)
def __call__(self, value1, value2):
return self.function(value1, value2)
This can be used directly:
op = Infix(lambda a, b: a + b) # can be any bivariate function
1 | op | 2
# 3
or as a decorator:
@Infix
def op(a, b):
return a + b
1 | op | 2
# 3
The above solution works as is, but there are some issues, e.g. op | 2
expression cannot be used alone:
op = Infix(lambda a, b: a + b)
(1 | op)
#<__main__.Infix object at 0x7facf8f33d30>
# (op | 2)
# TypeError: <lambda>() missing 1 required positional argument: 'b'
(1 | op | 2)
# 3
Binding Implementation
To get proper bindings one would need to write a bit more complex code performing an intermediate binding:
class Infix(object):
def __init__(self, func):
self.func = func
class RBind:
def __init__(self, func, binded):
self.func = func
self.binded = binded
def __call__(self, other):
return self.func(other, self.binded)
__ror__ = __call__
class LBind:
def __init__(self, func, binded):
self.func = func
self.binded = binded
def __call__(self, other):
return self.func(self.binded, other)
__or__ = __call__
def __or__(self, other):
return self.RBind(self.func, other)
def __ror__(self, other):
return self.LBind(self.func, other)
def __call__(self, value1, value2):
return self.func(value1, value2)
This is used the same way as before, e.g. either:
op = Infix(lambda a, b: a + b)
or as a decorator:
@Infix
def op(a, b):
return a + b
With this, one would get:
1 | op
# <__main__.Infix.LBind object at 0x7facf8f2b828>
op | 2
# <__main__.Infix.RBind object at 0x7facf8f2be10>
1 | op | 2
# 3
There is also a PyPI package (with which I have no affiliation) implementing substantially this: https://pypi.org/project/infix/
Timings
Incidentally, the binding solutions seems to be also marginally faster:
%timeit [1 | op | 2 for _ in range(1000)]
# Non-binding implementation
# 1000 loops, best of 3: 626 µs per loop
# Binding implementation
# 1000 loops, best of 3: 525 µs per loop
Notes
These implementations use |
, but any binary operator could be used:
+
:__add__
-
:__sub__
*
:__mul__
/
:__truediv__
//
:__floordiv__
%
:__mod__
**
:__pow__
@
:__matmul__
(for Python 3.5 onwards)|
:__or__
&
:__and__
^
:__xor__
>>
:__rshift__
<<
:__lshift__
The **
would require the binding implementation or adjusting the non-binding one to reflect that the operator is right-associative. All the other operators from above are either left-associative (-
, /
, //
, %
, @
, >>
, <<
) or directly commutative (+
, *
, |
, &
, ^
).
Remember that these would all have the same precedence as the normal Python operators, hence, e.g.:
(1 | op | 2 * 5) == (1 | op | (2 * 5)) != ((1 | op | 2) * 5)
Custom Search menu to add my own operators?
I found the answer with some help. Pretty much you need to use:
invoke_search_popup
You'll need to create an enum on an operator then run the invoke_search_popup on the operator.
Here is the example code from Gorgious, and Yilmazz:
https://blender.stackexchange.com/questions/247695/invoke-search-popup-for-a-simple-panel
import bpy
import re
import json
items = (("Metal", "Metal", ""),
("Plastic", "Plastic", ""),
("Glass", "Glass", ""),
("Shadow", "Shadow", ""),
)
PROPS = [
('material', bpy.props.PointerProperty(type=bpy.types.Material, name='Material')),
]
# == OPERATORS
class MYCAT_OT_search_popup(bpy.types.Operator):
bl_idname = "object.search_popup"
bl_label = "Material Renamer"
bl_property = "my_enum"
my_enum: bpy.props.EnumProperty(items = items, name='New Name', default=None)
@classmethod
def poll(cls, context):
return context.scene.material # This prevents executing the operator if we didn't select a material
def execute(self, context):
material = context.scene.material
material.name = self.my_enum
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
wm.invoke_search_popup(self)
return {'FINISHED'}
# == PANELS
class ObjectRenamerPanel(bpy.types.Panel):
bl_idname = 'VIEW3D_PT_object_renamer'
bl_label = 'Material Renamer'
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
def draw(self, context):
col = self.layout.column()
row = col.row()
row.prop(context.scene, "material")
col.operator('object.search_popup', text='Rename') #Display the search popup operator
# == MAIN ROUTINE
CLASSES = [
MYCAT_OT_search_popup,
ObjectRenamerPanel,
]
def register():
for (prop_name, prop_value) in PROPS:
setattr(bpy.types.Scene, prop_name, prop_value)
for klass in CLASSES:
bpy.utils.register_class(klass)
def unregister():
for (prop_name, _) in PROPS:
delattr(bpy.types.Scene, prop_name)
for klass in CLASSES:
bpy.utils.unregister_class(klass)
if __name__ == '__main__':
register()
What is programming language support define your own custom operator?
Neither C++ nor Python (C++, Python) support the overloading of "new" operators (although C++ does support overloading the new
operator). Overloading operators is just syntactic sugar though - you can always just define a member function, name it something that explains its behaviour better than a single character operator could, and call it instead.
There do exist languages where this is possible: for example Haskell, a beautiful language that does allow you to define your own operators, by using symbols for the name and surrounding them with parentheses, eg:
(!!!) :: [a] -> Integer -> a
(x:xl) !!! 0 = x
(_:xl) !!! n = xl !!! (n-1)
_ !!! _ = error "Index out of bounds"
(note this is just a redefinition of the standard !!
function, I'm unimaginative...)
Python: defining my own operators?
No, you can't create new operators. However, if you are just evaluating expressions, you could process the string yourself and calculate the results of the new operators.
Related Topics
Parsing Date with Timezone from an Email
Python Iterator Is Empty After Performing Some Action on It
Multiple Axis in Matplotlib with Different Scales
What Does Numpy.Random.Seed(0) Do
Generating an Md5 Checksum of a File
Importerror After Successful Pip Installation
How to Create a Set of Sets in Python
How to Index into a Dictionary
Pandas Dataframe Stored List as String: How to Convert Back to List
Python: Defining My Own Operators
Pandas: Converting to Numeric, Creating Nans When Necessary
Combine Pool.Map with Shared Memory Array in Python Multiprocessing