隨手扎
【微更】你可能沒看過得Python - Callable(續)
【微更】Callable的實現
之前我實現了Callable Class,當時對於CallableWrapper
的實現如下:
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))
這個實現有一些缺憾,CallableWrapper
的__call__
回傳值,也是CallableWrapper
。而CallableWrapper
本身預期被呼叫,這導致結果不能直接使用,需要多給一次空呼叫(result()
)。舉例來說:
arr = CallableWrapper([1,2,3,4,5,6,])
arr # => <__main__.CallableWrapper object at 0x7f2102f00668>
# arr(sum) + 10 # You can't do this, because CallableWrapper can't add integer
arr(sum)() + 10
上例中,並不能直接寫arr(sum) + 10
,要寫arr(sum)() + 10
。這感覺有點脫褲子放屁阿…
基於此想法,我改寫成:
class CallableWrapper:
def __init__(self, wrap):
self.wrap = wrap
def __call__(self, f=None, *args, **kargs):
if f == None:
return self.wrap
result = f(self.wrap, *args, **kargs)
class __callable(type(result), Callable):
pass
return __callable(result, *args, **kargs)
@staticmethod
def new(wrap, *args, **kargs):
class __callable(type(wrap), Callable):
pass
return __callable(wrap, *args, **kargs)
使用內部類別的方式實現,然後讓我有點意外的是Python竟然接受了這樣的寫法😆。
相對的,我把CallableMeta
的實現也改了一下:
class CallableMeta(type):
def __call__(defineclz, *args, **kargs):
class __callableClass:
def __call__(self, f = None, *args, **kargs):
if f == None: return self
result = f(self, *args, **kargs)
class __callable(type(result), Callable): pass
return __callable(result)
class __defineclz(defineclz, __callableClass):
pass
instance = __defineclz.__new__(__defineclz, *args, **kargs)
__defineclz.__init__(instance, *args, **kargs)
return instance
然後或許可以改成(不過我還沒試過):
class CallableMeta(type):
def __call__(defineclz, *args, **kargs):
class __callableClass:
def __call__(self, f = None, *args, **kargs):
if f == None: return self
result = f(self, *args, **kargs)
return CallableWrapper.new(result)
class __defineclz(defineclz, __callableClass):
pass
instance = __defineclz.__new__(__defineclz, *args, **kargs)
__defineclz.__init__(instance, *args, **kargs)
return instance
現在可以這樣寫了arr(sum) + 10
,要多做點事情也行arr(sum)(float) + 10
。
你可能沒看過得Python
恩~?
這怎麼有起來好像什麼東西?不就是我在這篇寫到蠻吸引我的:
2019.year + 12.month + 28.day
來使用看看
_ = CallableWrapper.new
def year(y:int): return y*(365.25)*24*60*60
def month(m:int): return m*30*24*60*60
def day(d:int): return d*24*60*60
def hour(h:int): return h*60*60
def minute(m:int): return m*60
def second(s:int): return s
_(2019)(year) + _(12)(month) + _(31)(day) # => 63748576800.0
上面其實全都用CallableWrapper.new
可能會更好看www。
※ C++有_n
operator可以做到類似的事情。
設計原因
class A:
pi = 3.14
def __getattr__(self, key: str, default = None):
print("getattr")
return None
def hello(self):
print("Hello, World")
a = A()
b = A()
a.a = 10
a.a # => 10
b.a # => Output: getattr. And Return: None
a.hello() # => Output: Hello, World
儘管__getattr__()
是只有在getattr()
找不到的時候,才會觸發,但這可能潛在一些問題。比如上例中。A
的類別屬性偌是不在__getattr__
處理會找不到(其實也就實現看起來有點麻煩拉😅)。其次,key
是string
,像是math.fsum
會找不到(會麻煩)。所以選擇更直接了當,而且一般資料不會使用的__call__
。
來看看EMACScript
本小節參考stack overflow的一個討論。
Integer.prototype.year = function(){return this*(365.25)*24*60*60;};
Integer.prototype.month = function(){return this*30*24*60*60;};
Integer.prototype.day = function(){return this*24*60*60;};
Integer.prototype.hour = function(){return this*60*60};
Integer.prototype.minute = function(){return this*60};
Integer.prototype.second = function(){return this};
// 2019.year() + 12.month() + 31.day() // sad... can't use on a immediately number.
var [y, m, d] = [2019, 12, 31]
y.year() + m.month() + d.day() // => 63748576800
不過同樣的,這樣做是危險的,對於核心的類型,無法保證是不是有其他人也這麼做,有可能衝突到。或許用with
可解(但with
同樣是不被推薦的,因為有可能造成一些混亂):
var DateFun = {
year(n){return n*(365.25)*24*60*60;},
month(n){return n*30*24*60*60;},
day(n){return n*24*60*60;},
hour(n){return n*60*60;},
minute(n){return n*60;},
second(n){return n;},
}
function hello(){
console.log("Hello, World");
}
with(DateFun){
year(2019) + month(12) + day(31); // => 63748576800
hello(); // can't find in DateFun, so use global function. Output: Hello, World
}
