moh*_*666 150 python methods call magic-methods
我知道__call__在调用类的实例时会触发类中的方法.但是,我不知道何时可以使用这种特殊方法,因为可以简单地创建一个新方法并执行在__call__方法中完成的相同操作,而不是调用实例,您可以调用该方法.
如果有人给我这种特殊方法的实际用法,我将非常感激.
S.L*_*ott 116
此示例使用memoization,基本上将值存储在表中(在本例中为字典),以便您可以在以后查找它们而不是重新计算它们.
这里我们使用一个带有__call__方法的简单类来计算阶乘(通过可调用对象)而不是包含静态变量的阶乘函数(因为这在Python中是不可能的).
class Factorial:
def __init__(self):
self.cache = {}
def __call__(self, n):
if n not in self.cache:
if n == 0:
self.cache[n] = 1
else:
self.cache[n] = n * self.__call__(n-1)
return self.cache[n]
fact = Factorial()
Run Code Online (Sandbox Code Playgroud)
现在你有一个fact可调用的对象,就像其他所有函数一样.例如
for i in xrange(10):
print("{}! = {}".format(i, fact(i)))
# output
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
Run Code Online (Sandbox Code Playgroud)
而且它也是状态.
Pra*_*ota 86
Django表单模块__call__很好地使用方法来实现表单验证的一致API.您可以在Django中为表单编写自己的验证器作为函数.
def custom_validator(value):
#your validation logic
Run Code Online (Sandbox Code Playgroud)
Django有一些默认的内置验证器,如电子邮件验证器,URL验证器等,它们大致属于RegEx验证器的范畴.为了干净利落地实现这些,Django转向可调用类(而不是函数).它在RegexValidator中实现默认的Regex Validation逻辑,然后扩展这些类以进行其他验证.
class RegexValidator(object):
def __call__(self, value):
# validation logic
class URLValidator(RegexValidator):
def __call__(self, value):
super(URLValidator, self).__call__(value)
#additional logic
class EmailValidator(RegexValidator):
# some logic
Run Code Online (Sandbox Code Playgroud)
现在,可以使用相同的语法调用自定义函数和内置EmailValidator.
for v in [custom_validator, EmailValidator()]:
v(value) # <-----
Run Code Online (Sandbox Code Playgroud)
如您所见,Django中的这种实现类似于其他人在下面的答案中解释的内容.这可以用其他方式实现吗?你可以,但恕我直言,对于像Django这样的大型框架,它不具有可读性或易于扩展性.
bra*_*ers 39
我发现它很有用,因为它允许我创建易于使用的API(你有一些需要一些特定参数的可调用对象),并且易于实现,因为你可以使用面向对象的实践.
以下是我昨天写的代码,它创建了hashlib.foo散列整个文件而不是字符串的方法的版本:
# filehash.py
import hashlib
class Hasher(object):
"""
A wrapper around the hashlib hash algorithms that allows an entire file to
be hashed in a chunked manner.
"""
def __init__(self, algorithm):
self.algorithm = algorithm
def __call__(self, file):
hash = self.algorithm()
with open(file, 'rb') as f:
for chunk in iter(lambda: f.read(4096), ''):
hash.update(chunk)
return hash.hexdigest()
md5 = Hasher(hashlib.md5)
sha1 = Hasher(hashlib.sha1)
sha224 = Hasher(hashlib.sha224)
sha256 = Hasher(hashlib.sha256)
sha384 = Hasher(hashlib.sha384)
sha512 = Hasher(hashlib.sha512)
Run Code Online (Sandbox Code Playgroud)
这个实现允许我以类似的方式使用hashlib.foo函数:
from filehash import sha1
print sha1('somefile.txt')
Run Code Online (Sandbox Code Playgroud)
当然我可以用不同的方式实现它,但在这种情况下,它看起来像一个简单的方法.
Kri*_*ris 21
__call__也用于在python中实现装饰器类.在这种情况下,调用带有装饰器的方法时会调用类的实例.
class EnterExitParam(object):
def __init__(self, p1):
self.p1 = p1
def __call__(self, f):
def new_f():
print("Entering", f.__name__)
print("p1=", self.p1)
f()
print("Leaving", f.__name__)
return new_f
@EnterExitParam("foo bar")
def hello():
print("Hello")
if __name__ == "__main__":
hello()
Run Code Online (Sandbox Code Playgroud)
小智 9
是的,当您知道自己正在处理对象时,使用显式方法调用是完全可能的(并且在许多情况下是可取的).但是,有时您处理需要可调用对象的代码 - 通常是函数,但是由于__call__您可以构建更复杂的对象,使用实例数据和更多方法来委派重复性任务等仍然可以调用.
此外,有时您将两个对象用于复杂任务(编写专用类有意义)和简单任务对象(已经存在于函数中,或者更容易编写为函数).要拥有一个通用接口,您必须编写包含具有预期接口的那些函数的小类,或者保留函数函数并使更复杂的对象可调用.我们以线程为例.Thread标准库模块中threading的对象需要一个可调用的target参数(即作为在新线程中完成的操作).对于可调用对象,您不限于函数,也可以传递其他对象,例如从其他线程获取任务并按顺序执行它们的相对复杂的工作程序:
class Worker(object):
def __init__(self, *args, **kwargs):
self.queue = queue.Queue()
self.args = args
self.kwargs = kwargs
def add_task(self, task):
self.queue.put(task)
def __call__(self):
while True:
next_action = self.queue.get()
success = next_action(*self.args, **self.kwargs)
if not success:
self.add_task(next_action)
Run Code Online (Sandbox Code Playgroud)
这只是我头脑中的一个例子,但我认为它已经足够复杂以保证课程.仅使用函数执行此操作很难,至少需要返回两个函数,而且这些函数正在慢慢变得复杂.一个可以重命名__call__到别的东西,并通过绑定方法,但是这使得代码稍微不太明显的创建线程,并且不会增加任何价值.
基于类的装饰器用于__call__引用包装函数.例如:
class Deco(object):
def __init__(self,f):
self.f = f
def __call__(self, *args, **kwargs):
print args
print kwargs
self.f(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
Artima.com上有各种选项的很好的描述