2010-07-01 24 views
42

Tôi có một lớp học như:__getattr__ cho các biến tĩnh/lớp trong python

class MyClass: 
    Foo = 1 
    Bar = 2 

Bất cứ khi nào MyClass.Foo hoặc MyClass.Bar được gọi, tôi cần một phương pháp tùy chỉnh được gọi trước khi giá trị được trả về. Có thể bằng Python? Tôi biết có thể nếu tôi tạo một thể hiện của lớp và tôi có thể định nghĩa phương thức __getattr__ của riêng mình. Nhưng scnenario của tôi liên quan đến việc sử dụng lớp này như vậy mà không tạo ra bất kỳ thể hiện nào của nó.

Ngoài ra tôi cần có phương thức tùy chỉnh __str__ để được gọi khi str(MyClass.Foo) được gọi. Python có cung cấp một tùy chọn như vậy không?

Trả lời

48

__getattr__()__str__() cho một đối tượng được tìm thấy trên lớp của nó, vì vậy nếu bạn muốn tùy chỉnh những thứ đó cho một lớp, bạn cần lớp học của một lớp. Một metaclass.

class FooType(type): 
    def _foo_func(cls): 
     return 'foo!' 

    def _bar_func(cls): 
     return 'bar!' 

    def __getattr__(cls, key): 
     if key == 'Foo': 
      return cls._foo_func() 
     elif key == 'Bar': 
      return cls._bar_func() 
     raise AttributeError(key) 

    def __str__(cls): 
     return 'custom str for %s' % (cls.__name__,) 

class MyClass: 
    __metaclass__ = FooType 


print MyClass.Foo 
print MyClass.Bar 
print str(MyClass) 

in:

foo! 
bar! 
custom str for MyClass 

Và không có, một đối tượng không thể chặn một yêu cầu cho một stringifying một trong những thuộc tính của nó. Đối tượng được trả về cho thuộc tính phải xác định hành vi __str__() của riêng nó.

+0

Cảm ơn Ignacio và Matt. Tôi nghĩ bây giờ tôi hiểu cách thức hoạt động của lớp (không phải ví dụ) và tôi cũng nghĩ rằng hành vi __str__ được yêu cầu rất khó đạt được. Tôi nghĩ metaclasses nên đủ tốt cho yêu cầu của tôi. :) – Tuxdude

+0

Nếu bạn luôn nhận được 'AttributeError' kiểm tra câu trả lời khác, có cú pháp cho python3 (ví dụ này có vẻ là python2) – Chris

8

Đầu tiên, bạn cần phải tạo một metaclass và xác định __getattr__() trên đó.

class MyMetaclass(type): 
    def __getattr__(self, name): 
    return '%s result' % name 

class MyClass(object): 
    __metaclass__ = MyMetaclass 

print MyClass.Foo 

Thứ hai, không. Gọi số str(MyClass.Foo) sẽ gọi số MyClass.Foo.__str__(), vì vậy bạn cần phải trả lại loại thích hợp cho MyClass.Foo.

+0

Thử nghiệm ví dụ này và nó chỉ hoạt động đối với tôi trong Python 2.x.Sau khi thay đổi bản in thành một hàm, Python 3.2 cho tôi một đối tượng "AttributeError: type 'MyClass" không có lỗi' Foo 'thuộc tính. Bất kỳ đề xuất? – CyberFonic

+1

@CyberED: http://docs.python.org/py3k/reference/datamodel.html#metaclasses: "Khi định nghĩa lớp được đọc, nếu đối số từ khóa' metaclass' được gọi được chuyển sau các căn cứ trong định nghĩa lớp, callable được gọi sẽ được gọi thay vì 'type()'. " –

+3

Không thể chờ đợi .... sau một chút Googling và thử nghiệm: Ví dụ này dành cho Python 2.x. Trong Python 3.x bạn cần: lớp MyClass (metaclass = MyMetaclass): vượt qua – CyberFonic

5

noone Ngạc nhiên chỉ một này ra:

class FooType(type): 
    @property 
    def Foo(cls): 
     return "foo!" 

    @property 
    def Bar(cls): 
     return "bar!" 

class MyClass(metaclass=FooType): 
    pass 

trình:

>>> MyClass.Foo 
'foo!' 
>>> MyClass.Bar 
'bar!' 

(cho Python 2.x, định nghĩa sự thay đổi của MyClass tới:

class MyClass(object): 
    __metaclass__ = FooType 

)

Các câu trả lời khác nói gì về str đúng cho giải pháp này: Nó phải được thực hiện trên loại thực sự trả về.

+2

Mặc dù tôi thường cố gắng không đăng bài "Cảm ơn", tôi nghiêm túc cần đăng "Cảm ơn" ngay bây giờ: Cảm ơn! – Chris

11

(Tôi biết đây là một câu hỏi cũ, nhưng kể từ khi tất cả các câu trả lời khác sử dụng một metaclass ...)

Bạn có thể sử dụng classproperty mô tả đơn giản như sau:

class classproperty(object): 
    """ @[email protected] """ 
    def __init__(self, f): 
     self.f = classmethod(f) 
    def __get__(self, *a): 
     return self.f.__get__(*a)() 

sử dụng nó như:

class MyClass(object): 

    @classproperty 
    def Foo(cls): 
     do_something() 
     return 1 

    @classproperty 
    def Bar(cls): 
     do_something_else() 
     return 2 
+0

Điều này sẽ chỉ hữu ích nếu bạn biết trước tên của các thuộc tính của mình. Nếu bạn đọc chúng từ tập tin và cần phải phản ứng động, điều này sẽ không hoạt động. – Chris

Các vấn đề liên quan