這不是一個很好的答案......更多的是代碼審查,所以我可能會等待不同的意見。
我有(個人而言......不客觀的確認這裏)看到__new__
通常使用的,當你用自己的__metaclass__
(ES)(檢查SO this answer和this great thread了Python的元類)
創造
class
實例
在你的榜樣,因爲如果你添加你要需要編輯Source
類的__new__
方法反正新源(新WhateverSrc()
啄),它看起來就像是一個有點矯枉過正使用一個類從BaseSource
繼承創造其他來源。此外,問題是:Source
類真的是BaseSource
?據我瞭解,不是真的... Source
是一個工廠的來源,對不對?如果是這樣的話,你可以試試this implementation,如果你願意的話(鏈接是我在第二段中提到的答案,所以我沒有太多的功勞來「發現」它),儘管工廠對我來說聽起來很Java。再次,這裏只是一個個人的觀點。
取而代之的是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
可以註冊在某種列表或字典的類(見this answer),這也可能給你一些想法:-)
在你的情況,下面的註冊通過元類一類的想法,下面的代碼片段工作,但你可以看到,代碼變得越來越撲朔迷離:
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:
(Neil_UK)要求在評論這個答案這將是更加令人困惑的OP ,大寫一些不是類的東西,或者調用一個非大寫的名字來實例化一個特定的對象?
開始之前,以下示例充分利用內置的type和vars函數。在繼續之前,你應該確保你熟悉他們的工作。
對我來說(這只是我的看法,因爲在Python中大寫或非大寫函數名在語法上都是可以的),它會以大寫字母形式顯示函數會更混亂。請記住,你實際上並沒有返回類(儘管你可以,因爲class
(es)也是type
類型的實例)你要返回的是實例,並且函數沒有錯誤(根據PEP8 naming convention的小寫)返回一個實例。這是記錄模塊做什麼,例如:
>>> 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'
字符串恰好是通過getLogger
函數創建的log
實例的name
屬性。好的... 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通過properties提供了「面具」 getter和setter的能力,我創建了一個可怕的混亂,根本不直觀。在處理Python時你會聽到很多次,事實上你可以做些什麼並不意味着你應該這樣做。我個人總是給本叔叔:能力越大責任重大(以及...或Voltaire,但咩,我發現引用本大叔冷卻器,whaddup!:-D?)
這說,你可能想創建一個用戶https://codereview.stackexchange.com/我敢肯定,有很多有知識的人可以比我更好地回答這類問題。
呵呵,之前我提過class
也是一個實例。等等,wootoot?是的。功能也是實例!!檢查了這一點:
>>> 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,我發現,它在很大程度上依賴於程序員的常識。雖然我最初猶豫不決,相信這種範例不會導致可怕的混亂,但事實證明它不(在大多數情況下,;-))。
Doh,一個函數可以返回一個對象,所以我認爲def create_source(choice):就是我以前的樣子。它不需要'__new__',它顯然不是一個BaseSource。這是我關於我的實現的另一個擔憂,你指出,我的Source並不是真正的BaseSource,儘管我發現ABC機制在其中發揮了很大的作用,所以我印象深刻。我認爲發生的事情是Source從其中一個子源發展而來,所以它開始作爲一個真正的源泉,而我陷入了錯誤的思維模式。 –
很高興我幫助**: - )**我喜歡元類......你可以用它們做很棒的事情......但是它們很棘手,可以很容易地導致*殺死蒼蠅與炮彈*情況 – BorrajaX
好吧,現在我還有一個問題,create_source()不是一個類,但它唯一的功能就是當被調用時,它返回一個特定類型的對象,就像一個類一樣,就像xxxSrc類一樣。我傾向於利用它,以便在其他地方調用它時,它看起來像一個類。哪一個會更混亂,大寫一些不是類的東西,或者調用一個非大寫的名字來實例化一個特定的對象? –