任何修补Python足够长的人都被以下问题咬伤(或撕成碎片):
def foo(a=[]):
a.append(5)
return a
Run Code Online (Sandbox Code Playgroud)
Python新手希望这个函数总能返回一个只包含一个元素的列表:[5].结果却非常不同,而且非常惊人(对于新手来说):
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
Run Code Online (Sandbox Code Playgroud)
我的一位经理曾经第一次遇到这个功能,并称其为该语言的"戏剧性设计缺陷".我回答说这个行为有一个潜在的解释,如果你不理解内部,那确实非常令人费解和意想不到.但是,我无法回答(对自己)以下问题:在函数定义中绑定默认参数的原因是什么,而不是在函数执行时?我怀疑经验丰富的行为有实际用途(谁真的在C中使用静态变量,没有繁殖错误?)
编辑:
巴泽克提出了一个有趣的例子.再加上你的大部分评论和特别是Utaal,我进一步阐述了:
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
Run Code Online (Sandbox Code Playgroud)
对我而言,似乎设计决策是相对于放置参数范围的位置:在函数内部还是"与它一起"?
在函数内部进行绑定意味着在调用函数时x有效地绑定到指定的默认值,而不是定义,这会产生一个深层次的缺陷:def在某种意义上,该行将是"混合"的(部分绑定)函数对象)将在定义时发生,并在函数调用时发生部分(默认参数的赋值).
实际行为更加一致:执行该行时,该行的所有内容都会得到评估,这意味着在函数定义中.
python language-design least-astonishment default-parameters
我想跟踪当前正在使用的某种类型的对象.例如:跟踪类的所有实例或由元类创建的所有类.
跟踪这样的实例很容易:
class A():
instances = []
def __init__(self):
self.instances.append(self)
Run Code Online (Sandbox Code Playgroud)
但是如果一个实例没有被引用到该列表之外的任何地方,则不再需要它,我不想在可能耗时的循环中处理该实例.
我尝试使用sys.getrefcount删除仅在列表中引用的对象.
for i in A.instances:
if sys.getrefcount(i) <=3: # in the list, in the loop and in getrefcount
# collect and remove after the loop
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是引用计数非常模糊.打开一个新的shell并创建一个没有内容的虚拟类,返回5
sys.getrefcount(DummyClass)
Run Code Online (Sandbox Code Playgroud)
另一个想法是复制对象然后删除列表并检查哪些对象已被安排用于垃圾收集,并在最后一步中删除这些对象.就像是:
Copy = copy(A.instances)
del A.instances
A.instances = [i for i in Copy if not copy_of_i_is_in_GC(i)]
Run Code Online (Sandbox Code Playgroud)
当引用计数变为0时,不必立即删除对象.我只是不想在不再使用的对象上浪费太多的资源.
我希望能够跟踪几何Point对象的实例,以便在自动命名新对象时知道已经"采用"了哪些名称.
例如,如果已创建名为"A","B"和"C"的点,则下一个自动命名的Point将命名为"D".如果名为"D"的Point被删除,或其引用丢失,则名称"D"再次可用.
我的主要属性Point的对象被定义为属性,并且是非常标准x,y和name.
我按照这里描述的那样继续使用weakref.WeakSet().我把它添加到我的Point班级:
# class attribute
instances = weakref.WeakSet()
@classmethod
def names_in_use(cls):
return {p.name for p in Point.instances}
Run Code Online (Sandbox Code Playgroud)
问题是,当我实现一个Point然后删除它时,大部分时间,但并不总是,从中移除Point.instances.我注意到,如果我运行测试套件(pytest -x -vv -r w),那么如果在测试中引发了某个异常,那么实例永远不会被删除(可能的解释在下面稍后阅读).
在下面的测试代码中,在第一次删除之后p,它总是被删除Point.instances,但在第二次删除之后p,它永远不会被删除(测试结果总是相同的),并且最后一个assert语句失败:
def test_instances():
import sys
p = Point(0, 0, 'A')
del p
sys.stderr.write('1 - Point.instances={}\n'.format(Point.instances))
assert len(Point.instances) == 0
assert Point.names_in_use() == set() …Run Code Online (Sandbox Code Playgroud) 我需要跟踪某些类的实例(并对这些类执行其他操作)。我希望不必在相关类中声明任何额外的代码,因此理想情况下所有内容都应该在元类中处理。
我不知道如何向这些类的每个新实例添加弱引用。例如:
class Parallelizable(type):
def __new__(cls, name, bases, attr):
meta = super().__new__(cls, name, bases, attr)
# storing the instances in this WeakSet
meta._instances = weakref.WeakSet()
return meta
@property
def instances(cls):
return [x for x in cls._instances]
class Foo(metaclass=Parallelizable)
def __init__(self, name):
super().__init__()
self.name = name
# I would like to avoid having to do that - instead have the metaclass manage it somehow
self._instances.add(self)
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?我似乎无法在元类方面找到一个钩子来进入__init__Foo 的......
class vehicles:
class car:
def drive():
pass
car1 = car()
car2 = car()
car3 = car()
Run Code Online (Sandbox Code Playgroud)
有没有办法可以在一次通话中调用所有车辆上的驱动功能,而无需明确指定每个车辆.
我想知道在做这样的事情时是否有任何错误(从OOP的角度来看):
class Foobar:
foobars = {}
def __init__(self, name, something):
self.name = name
self.something = something
Foobar.foobars[name] = self
Foobar('first', 42)
Foobar('second', 77)
for name in Foobar.foobars:
print name, Foobar.foobars[name]
Run Code Online (Sandbox Code Playgroud)
编辑:这是我正在使用的实际代码片段
from threading import Event
class Task:
ADDED, WAITING_FOR_DEPS, READY, IN_EXECUTION, DONE = range(5)
tasks = {}
def __init__(self, name, dep_names, job, ins, outs, uptodate, where):
self.name = name
self.dep_names = [dep_names] if isinstance(dep_names, str) else dep_names
self.job = job
self.where = where
self.done = Event()
self.status = Task.ADDED …Run Code Online (Sandbox Code Playgroud) 我创建了很多类的实例。然后我想通过名称找到一个实例。但我收到错误消息TypeError: get() missing 1 required positional argument: 'value'。
class Test(object):
def __init__(self, value):
self.value = value
def get(self, value):
if self.value == value:
return self
else:
return None
test_obj = Test('foobar')
print(test_obj.value)
instance = Test.get('foobar')
if instance:
print(instance.value)
Run Code Online (Sandbox Code Playgroud)