这不是一个完整的答案......更像是代码审查,所以我可能会等待不同的意见。
我(就个人而言......这里没有客观的确认)看到__new__
通常用于创建class
您使用自己的__metaclass__
(es)时的实例(检查SO中的这个答案和这个关于Python元类的精彩线程)
在您的示例中,因为如果您添加一个新的源(一个新的WhateverSrc()
东西),无论如何您都需要编辑__new__
您的类的方法,使用继承自的类来创建其他源Source
似乎有点过头了。BaseSource
另外,问题是:这个Source
类真的是 aBaseSource
吗?据我了解,并不是真的……Source
是资源工厂,对吧?如果是这种情况,你可以尝试这个实现,如果你愿意(链接是我在第二段中提到的答案,所以我没有太多“找到”它的优点),尽管工厂对我来说听起来非常 Java-esque。再次,这里只是个人意见。
Source(BaseSource)
我会使用一个简单的方法,而不是像你在那里那样的类create_source
:
## [ . . . ]
class RandomSrc(BaseSource):
def get(self):
return random.random()
def create_source(choice):
if choice == 0:
return ManualSrc()
elif choice == 1:
return RandomSrc()
else:
raise ValueError('source choice parameter {} not valid'.format(choice))
if __name__ == '__main__':
for use_src in range(4):
print 'using source choice {}'.format(use_src)
src = create_source(use_src)
print src.get()
如果您需要一个新来源,您可以编辑该create_source
方法,例如:
## [ . . . ]
class RandomSrc(BaseSource):
def get(self):
return random.random()
class WhateverSrc(BaseSource):
def get(self):
return "Foo Bar??"
def create_source(choice):
if choice == 0:
return ManualSrc()
elif choice == 1:
return RandomSrc()
elif choice == 2:
return WhateverSrc()
else:
raise ValueError('source choice parameter {} not valid'.format(choice))
甚至更多......@abstractmethod
完全忘记,只需获得一堆或常规的具体课程。如果某人创建了一个*Src
没有实现该get
方法的新类,那么该人无论如何都会看到一个非常描述性的失败......
import random
class ManualSrc(object):
def get(self):
return float(raw_input('gimme a number - '))
class RandomSrc(object):
def get(self):
return random.random()
class BreakingSrc(object):
pass
def create_source(choice):
if choice == 0:
return ManualSrc()
elif choice == 1:
return RandomSrc()
elif choice == 2:
return BreakingSrc()
else:
raise ValueError('source choice parameter {} not valid'.format(choice))
if __name__ == '__main__':
for use_src in range(4):
print 'using source choice {}'.format(use_src)
src = create_source(use_src)
print src.get()
输出:
using source choice 0
gimme a number - 1
1.0
using source choice 1
0.702223268052
using source choice 2
Traceback (most recent call last):
File "./stack26.py", line 28, in <module>
print src.get()
AttributeError: 'BreakingSrc' object has no attribute 'get'
说了这么多...使用元类,您可以在定义时在某种列表或字典中注册一个类class Whatever
(请参阅此答案),这也可以给您一些想法:-)
在您的情况下,遵循通过元类注册类的想法,下面的代码片段有效,但正如您所看到的,代码变得越来越混乱:
from abc import ABCMeta, abstractmethod
import random
import inspect
available_srcs = []
def register(newclass):
if inspect.isabstract(newclass):
print ("newclass %s is abstract, and has abstract"
" methods: %s. Refusing to register"
% (newclass, newclass.__abstractmethods__))
return
if newclass not in available_srcs:
available_srcs.append(newclass)
print "Registered %s as available source" % newclass
class MyMetaClass(ABCMeta):
def __new__(cls, clsname, bases, attrs):
newclass = super(MyMetaClass, cls).__new__(cls, clsname, bases, attrs)
register(newclass) # here is your register function
return newclass
class BaseSource(object):
__metaclass__ = MyMetaClass
@abstractmethod
def get(self):
pass
class ManualSrc(BaseSource):
def get(self):
return float(raw_input('gimme a number - '))
class RandomSrc(BaseSource):
def get(self):
return random.random()
if __name__ == '__main__':
for use_src in range(4):
print 'using source choice {}'.format(use_src)
src = available_srcs[use_src]()
print src.get()
编辑 1:
OP(Neil_UK)在对此答案的评论中询问哪个会更令人困惑,将不是类的东西大写,或调用非大写名称来实例化特定对象?
在开始之前,以下示例充分利用了内置的type和vars函数。在继续之前,您应该确保您熟悉他们的工作。
对我来说(这只是我的观点,因为大写或非大写的函数名称在 Python 中的语法上都可以),使用大写字母的函数会更令人困惑。请记住,您实际上并没有返回一个类(尽管您可以,因为class
(es) 也是类型的实例type
)您返回的是一个实例,并且函数没有任何问题(根据PEP8 ,小写命名约定)返回一个实例。这就是日志模块的作用,例如:
>>> import logging
>>> log = logging.getLogger('hello')
>>> vars(log)
{'name': 'hello', 'parent': <logging.RootLogger object at 0x17ce850>, 'handlers': [], 'level': 0, 'disabled': 0, 'manager': <logging.Manager object at 0x17ce910>, 'propagate': 1, 'filters': []}
>>> type(log)
<class 'logging.Logger'>
回到您的特定场景:如果我对您的代码一无所知(如果我只是在CreateSource
某个地方导入),并且我知道我必须这样使用CreateSource
:
src = CreateSource(use_src)
我会自动认为这是该类src
的一个实例,CreateSource
而且我在use_src
参数中传递的整数将存储在某个地方的属性中。检查logging
上面复制的示例......'hello'
字符串恰好是通过函数创建的实例的name
属性。好的......这个功能没有什么奇怪的。log
getLogger
getLogger
让我们来看一个极端的例子。我知道不是你做了我将要做的事情,(我认为你的问题实际上是一个有效的问题)但也许它会帮助证明我的意思。
考虑以下代码:
a = A()
a.x = 5
print "a.x is %s" % a.x
我你刚刚看到了,你认为那里发生了什么?你会认为你正在创建一个类 A 的空实例,并将其x
属性设置为5
,所以你会期望print
输出a.x is 5
,对吗?
错误的。这是正在发生的事情(完全正确的 Python):
class B(object):
def __init__(self):
self.x = 10
@property
def x(self):
return "I ain't returning x but something weird, and x is %s... FYI"\
% self._x
@x.setter
def x(self, x):
self._x = int(self._x if hasattr(self, '_x') else 0 + 2 * x)
def A():
return B()
所以a
实际上是一个实例,class B
并且由于 Python 提供了通过属性“屏蔽”getter 和 setter 的能力,我正在创造一个根本不直观的可怕混乱。在与 Python 打交道时,你会听到很多次你可以做某事的事实并不意味着你应该去做。我个人总是引用本叔叔的话:权力越大,责任越大(嗯......或伏尔泰,但是,我觉得引用本叔叔的话更酷,whaddup!!? :- D)
这就是说,您可能想在https://codereview.stackexchange.com/中创建一个用户,我相信有很多知识渊博的人可以比我更好地回答这类问题。
哦,在我提到之前,这class
也是一个例子。等等,呜呜呜??是的。函数也是实例!!看一下这个:
>>> class C(object):
... pass
...
>>> vars(C)
dict_proxy({'__dict__': <attribute '__dict__' of 'C' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None})
>>> type(C)
<type 'type'>
>>> def get_me_a_c_class():
... return C
...
>>> my_class = get_me_a_c_class()
>>> my_instance = my_class()
>>> type(my_instance)
<class '__main__.C'>
>>> type(get_me_a_c_class)
<type 'function'>
>>> vars(get_me_a_c_class)
{}
>>> get_me_a_c_class.random_attribute = 5
>>> print "Did I just put an attribute to a FUNCTION??: %s" % get_me_a_c_class.random_attribute
Did I just put an attribute to a FUNCTION??: 5
在我与 Python 打交道的几年中,我发现它严重依赖于程序员的常识。虽然我最初犹豫是否相信这种范式不会导致可怕的混乱,但事实证明它不会(在大多数情况下;-))。