map [Task] int64其中Task是一个接口(map[Task]int64 where Task is an interface)
假设我在Go库中定义了以下接口:
type Task interface { Do() error } func Register(task Task) { ... } func GetId(task Task) int64 { ... }
在
Register()
,库将唯一的int64
与每个任务实例相关联。GetId()
必须返回给定任务的标识符。我最初的想法是将关联存储为
map[Task]int64
。 这似乎工作正常,但有人告诉我,如果实现Task
的对象不具有可比性(例如,包含map
的struct
),它将会中断。 我仍然需要检查这是否属实。我打算尝试使用一块
struct { task Task; id int64 }
struct { task Task; id int64 }
而不是迭代它,但这仍然需要与可比较的Task
实例相等。 和AFAIU在Go中没有身份比较。如何从
Task
实例到其ID具有强大的映射?编辑:到目前为止提出的两个解决方案都有效,但它们的缺点是每个Task实现都必须包含一些重复的代码来处理ID。 我可以在可以嵌入的TaskBase
struct
中提供该代码,但理想情况下,我更喜欢一种不需要实现甚至知道ID的解决方案(它们是库内部的,并且在它之外没有任何意义)。Let's say I define the following interface in a Go library:
type Task interface { Do() error } func Register(task Task) { ... } func GetId(task Task) int64 { ... }
In
Register()
, the library associates a uniqueint64
with each task instance.GetId()
must return the identifier for the given task.My initial idea was to store the association as a
map[Task]int64
. This seems to work fine, but I was told that it would break if an object implementingTask
was not equality-comparable (for example, astruct
containing amap
). I still need to check if this is true.I was going to try and use a slice of
struct { task Task; id int64 }
instead and just iterate over it, but that would still require equality comparableTask
instances. And AFAIU there is no identity comparison in Go.How can I have a robust mapping from
Task
instances to their ID?EDIT: Both solutions proposed so far work, but they have the disadvantage that every Task implementation has to include some repetitive code to handle the IDs. I could provide that code in a TaskBase
struct
that could be embedded, but ideally I would prefer a solution that doesn't require implementations to even know about the IDs (they are internal to the library and have no meaning outside of it).
原文:https://stackoverflow.com/questions/13519152
最满意答案
您可以生成函数的
number
副本,每个副本都有独立的lru_cache
包装器:setup = '''\ from {name} import recursive_function as f f = iter([ functools.lru_cache(maxsize=None)(recursive_function.__wrapped__) for _ in range({number})]) n = {n} next_ = next '''.format(name=__name__, number=number, n=n) test = '''\ recursive_function = next_(f) recursive_funcion.__globals__['recursive_funcion'] = recursive_funcion recursive_function(n) ''' return timeit.timeit(test, setup, number=number)
该设置预先创建
number
独立装饰的功能对象,每个功能对象都有一个不同的LRU缓存,并为此创建一个迭代器。 然后,测试使用next()
函数获取下一个可用的函数对象,并将其用于测试。但是,每次都必须替换当前的全局名称
recursive_function
,否则递归调用将找不到新的装饰版本。 这有点不利,不要运行计时器并且期望缓存在之后是空的(它将包含最后一次测试运行的结果)。这是因为:
- 原始的非缓存函数仍可用作
recursive_function.__wrapped__
- decorator语法只是用于调用decorator对象以生成新函数对象的语法糖。
这为每个单独的测试提供了一个干净的缓存,只需要很少的开销(只有一个
next()
调用,它已被绑定到本地以避免全局名称查找惩罚)。You could produce
number
copies of the function, each with their independentlru_cache
wrapper:setup = '''\ from {name} import recursive_function as f f = iter([ functools.lru_cache(maxsize=None)(recursive_function.__wrapped__) for _ in range({number})]) n = {n} next_ = next '''.format(name=__name__, number=number, n=n) test = '''\ recursive_function = next_(f) recursive_funcion.__globals__['recursive_funcion'] = recursive_funcion recursive_function(n) ''' return timeit.timeit(test, setup, number=number)
The setup creates
number
individually decorated function objects up front, each with a distinct LRU cache, and creates an iterator for this. The test then uses thenext()
function to obtain the next available function object, and uses that for the test.You do have to replace the current global name
recursive_function
each time however, as otherwise the recursive call won't find the new decorated version. This is a bit of a downside, don't run the time trial and expect the cache to be empty afterwards (it'll contain the results of the very last test run instead).This works because:
- the original un-cached function is still available as
recursive_function.__wrapped__
- decorator syntax is just syntactic sugar for a call to the decorator object to produce a new function object.
This gives each individual test a clean cache, with minimal overhead (only a
next()
call, which has been bound to a local to avoid the global name lookup penalty).
相关问答
更多-
python timeit怎么用[2021-09-30]
python timeit 是 Python 的标准库; This module provides a simple way to time small bits of Python code. 先看一个例子吧: import timeit #执行命令 t2 = timeit.Timer('x=range(1000)') #显示时间 t2.timeit() #10.620039563513103 #执行命令 t1 = timeit.Timer('sum(x)', 'x = (i for i in range ... -
您将文件命名为timeit.py ,它会阻止内置模块,因此import timeit时间导入您自己的文件。 将文件命名为其他内容。 You named your file timeit.py, which blocks the builtin module, so import timeit is importing your own file. Name your file something else.
-
您可以像这样定义mySetup变量: mySetup = ''' import Statistics import random import hashingLibrary from CuckooHashing import * ''' 如果你只是考虑到这一点,那根本不是问题。 但是,这些行实际上出现在函数声明中: def recordCuckoo(amtElements, loadFactor): mySetup = ''' import Statistics import ra ...
-
如果你愿意考虑timeit替代品,我最近发现了秒表计时器工具,它可能对你的情况有用。 它也非常简单直观: import stopwatch class TimedClass(): def __init__(self): t = stopwatch.Timer() # do stuff here t.stop() print t.elapsed if you're willing to consider alternatives t ...
-
来自Python timeit的两个非常不同但非常一致的结果(Two very different but very consistent results from Python timeit)[2022-05-29]
结果发现这里发生了一些事情,虽然显然这些怪癖中的一些特定于我的机器,但是我认为值得发布它们以防有人对同一事物感到困惑。 首先,两个timeit函数之间有一个不同之处: timeit.timeit('math.e**2', 'import math', number=1000000) 导入语句被延迟加载。 如果您尝试以下实验,这变得很明显: timeit.timeit('1+1', 'import math', number=1000000) 与: timeit.timeit('1+1', number= ... -
替换timeit.timeit( 'val_in_range(x,x/2)', 'from __main__ import val_in_range, x', number=10) with timeit.timeit(lambda:val_in_range(x,x/2), number=10) 您可以使用print语句直接打印该值。 replace timeit.timeit( 'val_in_range(x,x/2)', 'from __main__ import val_in_range, x', n ...
-
您提供的字符串被解释为源代码,因此您可以使用带有三个引号的多行字符串,例如 >>> timeit.timeit(stmt = """if True: 'hi' ... else: 'bye'""") 0.015218939913108187 或 \n换行(但看起来很乱) >>> timeit.timeit(stmt = "if True: 'hi'\nelse: 'bye'") 0.015617805548572505 如果只需要一个分支,也可以使用三元if-else条件(因此不需要换行): >>> t ...
-
timeit偶尔会返回一个负值,可能是timeit中的错误(timeit returning a negative value occasionally, possible bug in timeit)[2022-12-02]
在Windows上,timeit默认使用time.clock作为其时间源,而后者又使用Windows API QueryPerformanceCounter 。 根据Windows的版本和机器的功能, QueryPerformanceCounter使用处理器的时间戳计数器(TSC)作为计时器。 许多较旧的多处理器计算机无法使TSC在处理器之间保持同步,并且没有正确地向Windows报告,或者有错误尝试这样做。 这导致QueryPerformanceCounter返回的结果似乎随着进程在不同的处理器上执行而跳 ... -
Python timeit.timeit在语句之间执行不定时的操作(Python timeit.timeit do untimed actions in between statements)[2024-01-25]
您可以生成函数的number副本,每个副本都有独立的 lru_cache包装器: setup = '''\ from {name} import recursive_function as f f = iter([ functools.lru_cache(maxsize=None)(recursive_function.__wrapped__) for _ in range({number})]) n = {n} next_ = next '' ... -
您可以在setup导入它们: timeit.timeit(stmt=s, setup='from __main__ import alpha_beta, vect2, alpha', number=100) 从Python 3.5开始, timeit.timeit也可以在提供的globals名称空间中执行代码。 将其设置为globals()将使stmt能够访问模块名称空间中的名称: timeit.timeit(stmt=s, number=100, globals=globals()) You coul ...