Implement CallableMeta and CallableCalss in Python
這篇文章同步發表中文版本。
Preface
Recently, I have written LeetCode with friends. Looking at their codes let me get more insight to solve problems.
938. Range Sum of BST
URL: https://leetcode.com/problems/range-sum-of-bst/
Inspired by a Ruby code which wrote by my friend, I wrote the same logical code with Python, as shown below.
Off cause, the problem has more quickly, and more simple solution, but this is more interesting.
But, the inner function `block` is dangerous or called “dirty” from Functional Programming.
That because of the function change the outer variable `s` when it executing. (Not enough pure functional)
So, I edited the code below.
or combine to one line.
You can easily change in-order travel to post-order or pre-order, and it’s safety.
771. Jewels and Stones
URL: https://leetcode.com/problems/jewels-and-stones
This problem is easily solved with Python, but it hard to read.
Inspired by LiveScript’s backcall(`<-`), Clojure, cl-arrows(Common Lisp)(`->`), I wrote a pipe function to chain functions.
OK, now more easy to understand what the code doing.
- Filter characters
- Then, Count each unique character
- Last, Sum all count
Callable Class
Sometimes, the pipe function is hard to use. That because some function must wrap by lambda to pass arguments. If there is a way to pass arguments, and easily reading too, it must be better. So, I wrote a Class that can wrap data and makes instance callable. If the input function doesn’t exist (None), it will return the processed data.
class CallableWrapper:
def __init__(self, wrap):
self.wrap = wrap
def __call__(self, f=None, *args):
if f == None:
return self.wrap
return CallableWrapper(f(self.wrap, *args))
So, you can use like below.
print(CallableWrapper([1,2,3,4,5,6])
(sum, 10)
(float)
()) # => Oupte: 31.0
# or
(CallableWrapper([1,2,3,4,5,6])
(sum, 10)
(float)
()) # => (31.0,)
※ Note1: Since Python Syntax Limit, Sometimes you must using brackets “()” to wrap expression.
※ Note2: There is a built-in function call `callable`(downcase). It checks an instance is callable. If callable return True, else False.(So…`callable(CallableWrapper([1,2,3,4,5])) #=> True`)
Callable Meta-Class
But…how about the custom class? It’s possibly easy to make a new class is callable? Well, it’s possible, and I wrote a meta-class, as shown below..
class CallableList(list, Callable): # or class CallableList(list, metaclass = CallableMeta):
def hello(self):
print("Hello World")
a = CallableList([1,2,3,4,5,6]) # => CallableList(a)
print(callable(a)) # => True
print(a) # => [1,2,3,4,5,6]
print(a()) # => [1,2,3,4,5,6]
print(a(sum)()) # => 21
print(a(sum)
(lambda x,i: x+i, 50)()) # => 71
You can use `metaclass=CallableMeta` when designing a new class(or directly inherit `Callable`). Define class `__call__` to override CallableMeta behavior(That means it’s useless to assign CallableMeta as metaclass). And with `super()`, that like pre-do or post-do. (A little like the decorator)
At last
With CallableMeta Class, the instance seems like has dynamic methods.
If you have other opinions or found bugs, be relax to leave your opinion. After reading this article, you can read more :
- maybe, pydash has a similar way to do.
- You can go to GitHub Gist found all codes.
- Maybe, I will write and collect to a package for myself
- or to read the Chinese version of this article.