Python类是如何工作的?

4
我有一个来自boto框架的代码文件如下所示,所有的print语句都是我自己添加的,注释掉的那一行也是我的,其余的属于原作者。
我的问题是,在实例化一个类时,在Python中分配和实例化的顺序是什么?作者的以下代码假定在创建类的实例(例如调用__init__())时'DefaultDomainName'将存在,但这似乎并不是在我的测试中在OS X上的Python 2.5中的情况。
在Manager __init__()方法中,我的打印语句显示为“None”。在全局函数set_domain()中的打印语句在设置Manager.DefaultDomainName之前显示“None”,并在赋值后显示预期值“test_domain”。但是,在调用set_domain()后再次创建Manager实例时,__init__()方法仍然显示为“None”。
有谁能帮助我并解释这里发生了什么。非常感谢。谢谢。
# 版权所有 (c) 2006,2007,2008 Mitch Garnaat http://garnaat.org/ # # 特此授权,任何获得本软件及相关文档文件(以下简称“软件”)的人, # 可以自由地处理本软件,包括但不限于使用、复制、修改、合并、发布、分发、 # 授权和/或销售软件的副本,并允许获得软件的人这样做,前提是: # # # 上述版权声明和本许可声明应包含在所有副本或重要部分中。 # # 本软件按“原样”提供,不附带任何明示或暗示的保证, # 包括但不限于适销性、特定用途的适用性和非侵权性。在任何情况下, # 作者均不对任何索赔、损害或其他责任承担责任, # 不论是在合同、侵权行为或其他方面引起的, # 与本软件或使用或其他交易有关的。
import boto from boto.utils import find_class
class Manager(object):
DefaultDomainName = boto.config.get('Persist', 'default_domain', None)
def __init__(self, domain_name=None, aws_access_key_id=None, aws_secret_access_key=None, debug=0): self.domain_name = domain_name self.aws_access_key_id = aws_access_key_id self.aws_secret_access_key = aws_secret_access_key self.domain = None self.sdb = None self.s3 = None if not self.domain_name: print "1: %s" % self.DefaultDomainName print "2: %s" % Manager.DefaultDomainName self.domain_name = self.DefaultDomainName #self.domain_name = 'test_domain' if self.domain_name: boto.log.info('未设置SimpleDB域名,使用默认域名: %s' % self.domain_name) else: boto.log.warning('未设置SimpleDB域名,禁用持久性') if self.domain_name: self.sdb = boto.connect_sdb(aws_access_key_id=self.aws_access_key_id, aws_secret_access_key=self.aws_secret_access_key, debug=debug) self.domain = self.sdb.lookup(self.domain_name) if not self.domain: self.domain = self.sdb.create_domain(self.domain_name)
def get_s3_connection(self): if not self.s3: self.s3 = boto.connect_s3(self.aws_access_key_id, self.aws_secret_access_key) return self.s3
def get_manager(domain_name=None, aws_access_key_id=None, aws_secret_access_key=None, debug=0): return Manager(domain_name, aws_access_key_id, aws_secret_access_key, debug=debug)
def set_domain(domain_name): print "3: %s" % Manager.DefaultDomainName Manager.DefaultDomainName = domain_name print "4: %s" % Manager.DefaultDomainName def get_domain(): return Manager.DefaultDomainName def revive_object_from_id(id, manager): if not manager.domain: return None attrs = manager.domain.get_attributes(id, ['__module__', '__type__', '__lineage__']) try: cls = find_class(attrs['__module__'], attrs['__type__']) return cls(id, manager=manager) except ImportError: return None def object_lister(cls, query_lister, manager): for item in query_lister: if cls: yield cls(item.name) else: o = revive_object_from_id(item.name, manager) if o: yield o

请问您能否发布一下用于创建Manager对象并设置域的(最小)代码? - Miles
@Miles +1...巨大的版权块意味着你必须滚动才能看到代码,而且(你会注意到屏幕底部的小CC标志)完全没有必要。你不需要发布许可证(也可能无法执行)。 - Aaron Maenpaa
4个回答

9

一些Python笔记

当Python执行class块时,它会在遇到它们的同时创建该类的所有"属性"。它们通常是类变量以及函数(方法)等。

"Manager.DefaultDomainName"的值在类定义中遇到时被设置。这段代码仅仅运行一次 - 永远不会再运行。原因是它只是在"定义"名为"Manager"的类对象。

当类"Manager"的一个对象被实例化时,它是"Manager"类的一个实例。(可能听起来有点重复)要非常清楚,值为:

self.DefaultDomainName

不存在。按照类的规则,Python会说“嗯,这个对象实例上不存在,我会查看类对象”。因此,Python实际上在以下位置找到该值:

Manager.DefaultDomainName

# also referenced by
self.__class__.DefaultDomainName

为了阐明这一点,我们举个例子:类属性"Manager.DefaultDomainName"只会被创建一次、只能存在一次,并且一次只能保存一个值。
在上面的示例中,对每个值运行内置函数id():
print "1: %s" % id(self.DefaultDomainName)
print "2: %s" % id(Manager.DefaultDomainName)

您应该看到它们引用了完全相同的内存位置。


现在,回答原始问题(或者不回答)...通过阅读上面的代码,我不知道。我建议您尝试一些技巧来找出答案:

# Debug with pdb.  Follow every step of the process to ensure that you are 
# setting valeus as you thought, and that the code you thought would be 
# called is actually being called.  I've had many problems like this where 
# the error was in procedure, not in the actual code at hand.
import pdb; pdb.set_trace()

# check to see if id(Manager) is the same as id(self.__class__)

# in the set_domain() function:
# check to see what attributes you can see on Manager, 
# and if they match the attributes on Manager and self.__class__ in __init__

请在你弄清楚后更新这里。

1

就像gahooa所说的那样。 除此之外,我假设boto.config.get(...)返回None,这可能是因为您配置文件中的Persist部分中未定义default_domain键。

boto.configboto/__init__.py中定义为config = boto.pyami.config.Config()(基本上是这样)。boto.pyami.config.Config是标准ConfigParser.SafeConfigParser的子类,并查找由boto.pyami.BotoConfigLocations指定的配置文件位置,默认情况下,该列表包含/etc/boto.cfg$HOME/.boto。如果这两个位置都没有配置文件,则您将没有默认域名。


1

感谢大家的帮助。我找到了自己缺少的东西:

我正在使用的boto类的类定义包含Manager的类变量,这些类变量又作为默认值,如果没有Manager传递给这些类的__init__()。当导入包含这些类的模块时,我甚至没有考虑这些类变量会随着导入语句而计算。

那么,这些类变量Manager的self.domain_name值在调用set_domain()之前就已经从DefaultDomainName设置好了,由于我没有像ruds指出的那样设置配置文件,这个值是None。

所以,我必须稍微重新编写一下我的代码,但感谢大家帮助一个Python新手。


0

当你加载模块时,Python会逐行执行每一行代码。由于代码是在执行的过程中被执行的,因此类变量应该在加载时全部设置好。很可能你的boto.config.get函数只是返回了None。换句话说,是的,在实例变量之前分配所有类变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接