2015-09-10 21 views
5

我需要創建一個對象,以任何方式引發自定義異常UnusableObjectError(創建它時不應創建異常)。以任何方式引發異常的對象

a = UnusableClass()  # No error 
b = UnusableClass()  # No error 
a == 4     # Raises UnusableObjectError 
'x' in a    # Raises UnusableObjectError 
for i in a:    # Raises UnusableObjectError 
    print(i) 

# ..and so on 

我想出了下面這似乎像預期的代碼。

class UnusableObjectError(Exception): 
    pass 

CLASSES_WITH_MAGIC_METHODS = (str(), object, float(), dict()) 

# Combines all magic methods I can think of. 
MAGIC_METHODS_TO_CHANGE = set() 
for i in CLASSES_WITH_MAGIC_METHODS: 
    MAGIC_METHODS_TO_CHANGE |= set(dir(i)) 
MAGIC_METHODS_TO_CHANGE.add('__call__') 
# __init__ and __new__ must not raise an UnusableObjectError 
# otherwise it would raise error even on creation of objects. 
MAGIC_METHODS_TO_CHANGE -= {'__class__', '__init__', '__new__'} 


def error_func(*args, **kwargs): 
    """(nearly) all magic methods will be set to this function.""" 
    raise UnusableObjectError 


class UnusableClass(object): 
    pass 

for i in MAGIC_METHODS_TO_CHANGE: 
    setattr(UnusableClass, i, error_func) 

(一些改進而成,由Duncan的意見建議)


問題:
有描述,其行爲已經存在的類?

如果不是,我的UnusableClass()中是否有任何缺陷(例如,在使用類的實例時不會產生錯誤),如果是這樣,我該如何解決這些缺陷?

+3

設置'__'如你期望在一個實例方法一般不會工作。你應該直接在課堂上定義它們。重新定義'__getattribute__'阻止訪問任何其他比你要允許很可能會做你嘗試什麼簡單的方法的那些屬性。 – Duncan

+1

一個例子,你希望這是行不通的,嘗試調用一個實例:一'()''給類型錯誤:「UnusableClass」對象不callable'因爲實例設置'__call__'被忽略。同樣'a [1] = 0'。當然,因爲這些沒有實現,他們仍然會拋出一個錯誤,而不是你的自定義錯誤。 在類上定義可以使用'def'或者只在類上使用'setattr'而不是在實例上。 – Duncan

+0

這也似乎是一個完美的用例爲元類... – thebjorn

回答

2

原來元類和dunder(雙下劃線)方法不順利在一起(這是不幸的,因爲這將是實現這個更精簡的方式)。

我找不到魔術方法名稱中的任意導入的上市,所以我創建一個並把它PyPI上(https://pypi.python.org/pypi/magicmethods/0.1.1)。有了它,UnusableClass的實施可以寫成一個簡單的類裝飾:

import magicmethods 

class UnusableObjectError(Exception): 
    pass 

def unusable(cls): 
    def _unusable(*args, **kwargs): 
     raise UnusableObjectError() 

    for name in set(magicmethods.all) - set(magicmethods.lifecycle): 
     setattr(cls, name, _unusable) 
    return cls 

@unusable 
class UnusableClass(object): 
    pass 

magicmethods.lifecycle包含__new____init____del__。您可能要調整這個..

此實現也處理:

a = UnusableClass() 
with a: 
    print 'oops' 
1

您可以使用__getattribute__阻止所有訪問屬性,除特殊__屬性像__contains____eq__未通過__getattribute__逮住,並使用whitelist允許訪問一些方法:

class UnuseableClass(object): 

    whitelist = ('alpha', 'echo',) 

    def __init__(self): 
     self.alpha = 42 

    def echo(self, text): 
     print text 

    def not_callable(self): 
     return 113 

    def __getattribute__(self, name): 
     if name in type(self).whitelist: 
      return super(UnuseableClass, self).__getattribute__(name) 
     else: 
      raise Exception('Attribute is not useable: %s' % name) 


unuseable_object = UnuseableClass() 
print(unuseable_object.alpha) 
unuseable_object.echo('calling echo') 

try: 
    unuseable_object.not_callable() 
except Exception as exc: 
    print(exc.message) 

如果你真的需要捕捉,甚至特殊的方法調用,您可以使用How to catch any method called on an object in python?