2011-12-16 14 views
11

Giả sử chúng ta có hệ thống phân cấp lớp sau đây:Có cách nào để truy cập __dict__ (hoặc một cái gì đó giống như nó) bao gồm các lớp cơ sở?

class ClassA: 

    @property 
    def foo(self): return "hello" 

class ClassB(ClassA): 

    @property 
    def bar(self): return "world" 

Nếu tôi khám phá __ dict __ trên ClassB như vậy, tôi chỉ nhìn thấy các thuộc tính thanh:

for name,_ in ClassB.__dict__.items(): 

    if name.startswith("__"): 
     continue 

    print(name) 

Output là thanh

Tôi có thể cuộn phương tiện của riêng mình để nhận các thuộc tính không chỉ về loại được chỉ định mà còn là tổ tiên của nó. Tuy nhiên, câu hỏi của tôi là liệu đã có một cách trong python cho tôi để làm điều này mà không tái phát minh ra một bánh xe.

def return_attributes_including_inherited(type): 
    results = [] 
    return_attributes_including_inherited_helper(type,results) 
    return results 

def return_attributes_including_inherited_helper(type,attributes): 

    for name,attribute_as_object in type.__dict__.items(): 

     if name.startswith("__"): 
      continue 

     attributes.append(name) 

    for base_type in type.__bases__: 
     return_attributes_including_inherited_helper(base_type,attributes) 

Chạy mã của tôi như sau ...

for attribute_name in return_attributes_including_inherited(ClassB): 
    print(attribute_name) 

... cho lại cả quầy bar và foo. Lưu ý rằng tôi đang đơn giản hoá một số thứ: va chạm tên, sử dụng các mục() khi cho ví dụ này tôi có thể sử dụng dict, bỏ qua bất kỳ thứ gì bắt đầu bằng __, bỏ qua khả năng hai tổ tiên có tổ tiên chung, v.v. .

EDIT1 - Tôi đã cố giữ ví dụ đơn giản. Nhưng tôi thực sự muốn cả tên thuộc tính và tham chiếu thuộc tính cho mỗi lớp và lớp tổ tiên. Một trong những câu trả lời dưới đây có tôi theo dõi tốt hơn, tôi sẽ đăng một số mã tốt hơn khi tôi làm cho nó hoạt động.

EDIT2 - Điều này làm những gì tôi muốn và rất ngắn gọn. Nó dựa trên câu trả lời của Eli dưới đây.

def get_attributes(type): 

    attributes = set(type.__dict__.items()) 

    for type in type.__mro__: 
     attributes.update(type.__dict__.items()) 

    return attributes 

Nó cung cấp cả tên thuộc tính và tham chiếu của chúng.

EDIT3 - Một trong các câu trả lời bên dưới được đề xuất sử dụng inspect.getmembers. Điều này xuất hiện rất hữu ích bởi vì nó giống như dict chỉ nó hoạt động trên các lớp tổ tiên là tốt.

Kể từ khi một phần lớn của những gì tôi đang cố gắng làm là tìm các thuộc tính được đánh dấu bằng một mô tả đặc biệt, và bao gồm các lớp tổ tiên, đây là một số mã mà có thể giúp làm điều đó trong trường hợp nó giúp mọi người:

class MyCustomDescriptor: 

    # This is greatly oversimplified 

    def __init__(self,foo,bar): 
     self._foo = foo 
     self._bar = bar 
     pass 

    def __call__(self,decorated_function): 
     return self 

    def __get__(self,instance,type): 

     if not instance: 
      return self 

     return 10 

class ClassA: 

    @property 
    def foo(self): return "hello" 

    @MyCustomDescriptor(foo="a",bar="b") 
    def bar(self): pass 

    @MyCustomDescriptor(foo="c",bar="d") 
    def baz(self): pass 

class ClassB(ClassA): 

    @property 
    def something_we_dont_care_about(self): return "world" 

    @MyCustomDescriptor(foo="e",bar="f") 
    def blah(self): pass 

# This will get attributes on the specified type (class) that are of matching_attribute_type. It just returns the attributes themselves, not their names. 
def get_attributes_of_matching_type(type,matching_attribute_type): 

    return_value = [] 

    for member in inspect.getmembers(type): 

     member_name = member[0] 
     member_instance = member[1] 

     if isinstance(member_instance,matching_attribute_type): 
      return_value.append(member_instance) 

    return return_value 

# This will return a dictionary of name & instance of attributes on type that are of matching_attribute_type (useful when you're looking for attributes marked with a particular descriptor) 
def get_attribute_name_and_instance_of_matching_type(type,matching_attribute_type): 

    return_value = {} 

    for member in inspect.getmembers(ClassB): 

     member_name = member[0] 
     member_instance = member[1] 

     if isinstance(member_instance,matching_attribute_type): 
      return_value[member_name] = member_instance 

    return return_value 
+0

Chính xác bạn sẽ làm gì với danh sách thuộc tính khi bạn có nó? –

+0

Các lớp được đề cập đại diện cho các thư đã được deserialized. Các tin nhắn sử dụng thừa kế khi chúng chia sẻ các trường. Có một phương thức cần in thông tin về từng trường của từng thư. Mỗi thuộc tính của các lớp thông điệp này thực sự được đánh dấu bằng bộ mô tả nhưng tôi đã để lại chi tiết đó vì tôi nghĩ nó không liên quan. –

+0

... Vì vậy, hãy cập nhật bộ mô tả để nó thêm thuộc tính được mô tả vào danh sách? Hoặc - và điều này có thể là một chút quá cấp tiến - sử dụng một dict với các phím chuỗi thay vì các thuộc tính riêng biệt? –

Trả lời

9

Bạn nên sử dụng mô-đun inspect của python cho bất kỳ khả năng nội suy nào như vậy.

. 
. 
>>> class ClassC(ClassB): 
...  def baz(self): 
...   return "hiya" 
... 
>>> import inspect 
>>> for attr in inspect.getmembers(ClassC): 
... print attr 
... 
('__doc__', None) 
('__module__', '__main__') 
('bar', <property object at 0x10046bf70>) 
('baz', <unbound method ClassC.baz>) 
('foo', <property object at 0x10046bf18>) 

Đọc thêm về inspect mô-đun here.

+0

Ngọt ngào, nó giống như một phiên bản tốt hơn của dict bởi vì trong khi dict chỉ hoạt động trên lớp, inspect.getmembers xuất hiện để hoạt động trên tổ tiên là tốt. Tôi sẽ đăng một số mẫu mã bổ sung trên phương pháp tiếp cận ban đầu của tôi bằng cách sử dụng kỹ thuật này. –

5

Bạn muốn sử dụng dir:

for attr in dir(ClassB): 
    print attr 
+0

Thú vị. Tôi cho rằng dir() vừa trả lại chìa khóa từ __dict__ nhưng bạn nói đúng, một khác biệt chính là dir() hoạt động trên lớp và tất cả tổ tiên của nó trong khi __dict__ dường như chỉ hoạt động trên lớp (không phải tổ tiên của nó). Tôi cần truy cập vào các thuộc tính (không chỉ là tên) nhưng có vẻ như tôi có thể sử dụng kết hợp dir() và getattr(). Cảm ơn! –

+1

Yep 'dir()' và 'gettattr()' là cách bình thường để thực hiện điều đó. – zeekay

+0

Sidenote: Mô-đun 'kiểm tra' là những gì tôi tin rằng lệnh dir() được xây dựng dựa trên. Tôi nghĩ rằng nó sử dụng mro và getmembers() để tìm "tất cả các thuộc tính có thể truy cập" của đối tượng. – jdi

2

Đáng buồn là không có một đối tượng hỗn hợp nào. Mỗi truy cập thuộc tính cho một đối tượng python (bình thường) đầu tiên kiểm tra obj.__dict__, sau đó là các thuộc tính của tất cả các lớp cơ sở của nó; trong khi có một số cache và tối ưu hóa nội bộ, không có một đối tượng duy nhất bạn có thể truy cập.

Điều đó nói rằng, có một điều mà có thể cải thiện mã của bạn là sử dụng cls.__mro__ thay vì cls.__bases__ ... thay vì bố mẹ ngay lập tức của lớp, cls.__mro__ chứa TẤT CẢ các tổ tiên của lớp, theo thứ tự chính xác Python sẽ tìm kiếm, với tất cả tổ tiên chung chỉ xảy ra một lần. Điều đó cũng sẽ cho phép phương pháp tìm kiếm kiểu của bạn không được đệ quy. Loosely ...

def get_attrs(obj): 
    attrs = set(obj.__dict__) 
    for cls in obj.__class__.__mro__: 
     attrs.update(cls.__dict__) 
    return sorted(attrs) 

...thực hiện gần đúng công bằng việc triển khai dir(obj) mặc định.

+0

Ồ, thật tuyệt khi biết! Có vẻ như tôi có hai lựa chọn. zeekay đề xuất sử dụng dir() (bao gồm tên thuộc tính của lớp tổ tiên) mặc dù sau đó tôi cần phải getattr() trên mỗi tên. Hoặc tôi có thể sử dụng phương pháp của bạn. Ví dụ của tôi làm cho nó xuất hiện mà tôi chỉ cần các tên thuộc tính nhưng trong thực tế tôi cần tham chiếu đến các thuộc tính mình. Vì vậy, tôi nghĩ rằng tôi sẽ sử dụng mã của bạn. Cảm ơn! –

+0

Ngoài ra, cảm ơn cho mẹo trên __mro__. Tôi nghĩ rằng tôi đã nhìn thấy ai đó sử dụng nó nhưng tôi đã không nhận ra nó là gì. Rất tiện dụng! –

+0

Vì vậy, điều này chỉ trả lại tên thuộc tính, phải không? Tôi đang làm việc ngay bây giờ khi sửa đổi nó để trả về cả tên thuộc tính và tham chiếu của chúng. Nếu bạn đã có một cái gì đó trong tâm trí để làm điều đó, bạn có thể gửi nó? –

1

Đây là chức năng tôi đã viết, quay lại trong ngày. Câu trả lời hay nhất là sử dụng mô-đun inspect, khi sử dụng __dict__ cung cấp cho chúng tôi TẤT CẢ các hàm (thành phần của chúng ta + được kế thừa) và (ALL?) Thành viên và thuộc tính dữ liệu. Trường hợp inspect cung cấp cho chúng tôi đủ thông tin để loại bỏ những gì chúng tôi không muốn.

def _inspect(a, skipFunctionsAlways=True, skipMagic = True): 
    """inspects object attributes, removing all the standard methods from 'object', 
    and (optionally) __magic__ cruft. 

    By default this routine skips __magic__ functions, but if you want these on 
    pass False in as the skipMagic parameter. 

    By default this routine skips functions, but if you want to see all the functions, 
    pass in False to the skipFunctionsAlways function. This works together with the 
    skipMagic parameter: if the latter is True, you won't see __magic__ methods. 
    If skipFunctionsAlways = False and skipMagic = False, you'll see all the __magic__ 
    methods declared for the object - including __magic__ functions declared by Object 

    NOT meant to be a comprehensive list of every object attribute - instead, a 
    list of every object attribute WE (not Python) defined. For a complete list 
    of everything call inspect.getmembers directly""" 

    objType = type(object) 
    def weWantIt(obj): 
     #return type(a) != objType 
     output= True 
     if (skipFunctionsAlways): 
      output = not (inspect.isbuiltin(obj)) #not a built in 

     asStr = "" 
     if isinstance(obj, types.MethodType): 
      if skipFunctionsAlways: #never mind, we don't want it, get out. 
       return False 
      else: 
       asStr = obj.__name__ 
       #get just the name of the function, we don't want the whole name, because we don't want to take something like: 
       #bound method LotsOfThings.bob of <__main__.LotsOfThings object at 0x103dc70> 
       #to be a special method because it's module name is special 
       #WD-rpw 02-23-2008 

       #TODO: it would be great to be able to separate out superclass methods 
       #maybe by getting the class out of the method then seeing if that attribute is in that class? 
     else: 
      asStr = str(obj) 

     if (skipMagic): 
      output = (asStr.find("__") == -1) #not a __something__ 

     return (output) 

    for value in inspect.getmembers(a, weWantIt): 
     yield value 
+0

Cảm ơn bạn đã đăng bài đó! –

0
{k: getattr(ClassB, k) for k in dir(ClassB)} 

giá trị đúng (thay vì <property object...>) sẽ được trình bày khi sử dụng ClassB dụ.

Và tất nhiên bạn có thể lọc bộ lọc này bằng cách thêm những thứ như if not k.startswith('__') vào cuối.

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