Nhận thuộc tính của một lớp


106

Tôi muốn lấy các thuộc tính của một lớp, hãy nói:

class MyClass():
  a = "12"
  b = "34"

  def myfunc(self):
    return self.a

sử dụng MyClass.__dict__cung cấp cho tôi danh sách các thuộc tính và chức năng, thậm chí cả các chức năng như __module____doc__. Trong khi MyClass().__dict__cung cấp cho tôi một mệnh lệnh trống trừ khi tôi đặt rõ ràng một giá trị thuộc tính của trường hợp đó.

Tôi chỉ muốn các thuộc tính, trong ví dụ trên, chúng sẽ là: ab


Câu trả lời:


123

Hãy thử mô-đun kiểm tra . getmembersvà các thử nghiệm khác nhau sẽ hữu ích.

BIÊN TẬP:

Ví dụ,

class MyClass(object):
    a = '12'
    b = '34'
    def myfunc(self):
        return self.a

>>> import inspect
>>> inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
[('__class__', type),
 ('__dict__',
  <dictproxy {'__dict__': <attribute '__dict__' of 'MyClass' objects>,
   '__doc__': None,
   '__module__': '__main__',
   '__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
   'a': '34',
   'b': '12',
   'myfunc': <function __main__.myfunc>}>),
 ('__doc__', None),
 ('__module__', '__main__'),
 ('__weakref__', <attribute '__weakref__' of 'MyClass' objects>),
 ('a', '34'),
 ('b', '12')]

Giờ đây, các phương thức và thuộc tính đặc biệt khiến tôi lo lắng - chúng có thể được xử lý theo một số cách, trong đó dễ nhất là lọc dựa trên tên.

>>> attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
>>> [a for a in attributes if not(a[0].startswith('__') and a[0].endswith('__'))]
[('a', '34'), ('b', '12')]

... và phức tạp hơn trong số đó có thể bao gồm kiểm tra tên thuộc tính đặc biệt hoặc thậm chí là kính đo;)


vâng, điều này thật tuyệt! Tôi đã sử dụng cái này: attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))) print [a[0] for a in attributes if '_' not in a[0]]
Mohamed Khamis

2
Hãy cẩn thận- điều đó sẽ không bao gồm các thuộc tính like_this! Nó cũng sẽ tránh các thuộc tính "riêng tư" mà bạn có thể đã cố tình làm.
Matt Luongo

Xin chào, tôi cũng yêu thích điều đó với một sự giải thích nhỏ: trong biểu thức inspect.getmembers(MyClass, ..., MyClasscó thể được thay thế bằng một lớp hoặc một đối tượng và nếu bạn cần danh sách các giá trị của các đối tượng, bạn phải thay thế MyClassbằng biến đối tượng của mình (hoặc selfnếu bạn đặt biểu thức này trong một def __repr__()phương pháp như tôi).
herve-guerin

Tôi đã sử dụng điều này (trong Python3) để nhận một hàm tìm kiếm giá trị ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]và sau đó chỉ là vấn đề lấy các khóa từ z.
double0darbo

42
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)

7
Điều này sẽ bao gồm tên phương pháp
lenhhoxung

10
Không rõ ràng hơn để kiểm tra: if not i.startswith('_')thay vì if i[:1] != '_'?
Mikaelblomkvistsson

2
Lưu ý: nếu chúng ta nói về lớp con (kế thừa), thì .__dict__.keys()sẽ không bao gồm các thuộc tính từ cha.
vishes_shell

21

myfunc một thuộc tính của MyClass. Đó là cách nó được tìm thấy khi bạn chạy:

myinstance = MyClass()
myinstance.myfunc()

Nó tìm kiếm một thuộc tính myinstancecó tên myfunc, không tìm thấy một thuộc tính nào, thấy đó myinstancelà một thể hiện của MyClassvà tìm kiếm nó ở đó.

Vì vậy, danh sách đầy đủ các thuộc tính cho MyClasslà:

>>> dir(MyClass)
['__doc__', '__module__', 'a', 'b', 'myfunc']

(Lưu ý rằng tôi đang sử dụng dir chỉ như một cách nhanh chóng và dễ dàng để liệt kê các thành viên của lớp: nó chỉ nên được sử dụng theo kiểu khám phá, không phải trong mã sản xuất)

Nếu bạn chỉ muốn thuộc tính cụ thể, bạn sẽ cần phải lọc danh sách này sử dụng một số tiêu chí, bởi vì __doc__, __module__myfunckhông phải là đặc biệt trong bất kỳ cách nào, họ thuộc tính đang ở chính xác cùng một cách mà abđang có.

Tôi chưa bao giờ sử dụng mô-đun kiểm tra được nhắc đến bởi Matt và Borealid, nhưng từ một liên kết ngắn gọn, có vẻ như nó có các bài kiểm tra để giúp bạn làm điều này, nhưng bạn sẽ cần phải viết hàm vị ngữ của riêng mình, vì nó có vẻ như những gì bạn muốn đại khái là các thuộc tính không vượt qua isroutinebài kiểm tra và không bắt đầu và kết thúc bằng hai dấu gạch dưới.

Cũng lưu ý: bằng cách sử dụng class MyClass():trong Python 2.7, bạn đang sử dụng các lớp kiểu cũ rất lỗi thời. Trừ khi bạn cố tình làm như vậy để tương thích với các thư viện cực kỳ cũ, thay vào đó bạn nên xác định lớp của mình là class MyClass(object):. Trong Python 3 không có lớp "kiểu cũ" và hành vi này là mặc định. Tuy nhiên, việc sử dụng các lớp kiểu mới sẽ giúp bạn có được nhiều thuộc tính được xác định tự động hơn:

>>> class MyClass(object):
        a = "12"
        b = "34"
        def myfunc(self):
            return self.a
>>> dir(MyClass)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'myfunc']

6
Người ta không thể phụ thuộc vào dir(): " Bởi vì dir () được cung cấp chủ yếu như là một tiện nghi để sử dụng ở một tương tác nhanh chóng, nó sẽ cố gắng để cung cấp một bộ thú vị của tên hơn nó cố gắng để cung cấp một tập hợp chặt chẽ hoặc liên tục được xác định tênchi tiết của nó hành vi có thể thay đổi qua các bản phát hành . "(xem tài liệu củadir() ).
Tadeck

@Tadeck: Điểm tốt. Tôi đang sử dụng nó một cách minh họa thay vì gợi ý nó như một giải pháp, vì nó sẽ không dễ dàng cho phép bạn lọc các thuộc tính dựa trên những gì chúng đề cập đến. Nhưng tôi nên nói rõ hơn về điều đó.
Ben

14

Chỉ lấy các thuộc tính cá thể là dễ dàng.
Nhưng việc lấy các thuộc tính của lớp mà không có các hàm thì khó hơn một chút.

Chỉ thuộc tính phiên bản

Nếu bạn chỉ phải liệt kê các thuộc tính thể hiện chỉ cần sử dụng
for attribute, value in my_instance. __dict__.items()

>>> from __future__ import (absolute_import, division, print_function)
>>> class MyClass(object):
...   def __init__(self):
...     self.a = 2
...     self.b = 3
...   def print_instance_attributes(self):
...     for attribute, value in self.__dict__.items():
...       print(attribute, '=', value)
...
>>> my_instance = MyClass()
>>> my_instance.print_instance_attributes()
a = 2
b = 3
>>> for attribute, value in my_instance.__dict__.items():
...   print(attribute, '=', value)
...
a = 2
b = 3

Thuộc tính phiên bản và lớp

Để nhận được các thuộc tính của lớp mà không có hàm, mẹo là sử dụng callable().

Nhưng phương pháp tĩnhkhông phải lúc nàocallable !

Do đó, thay vì sử callable(value)dụng sử dụng
callable( getattr(MyClass, attribute))

Thí dụ

from __future__ import (absolute_import, division, print_function)

class MyClass(object):
   a = "12"
   b = "34"               # class attributes

   def __init__(self, c, d):
     self.c = c
     self.d = d           # instance attributes

   @staticmethod
   def mystatic():        # static method
       return MyClass.b

   def myfunc(self):      # non-static method
     return self.a

   def print_instance_attributes(self):
     print('[instance attributes]')
     for attribute, value in self.__dict__.items():
        print(attribute, '=', value)

   def print_class_attributes(self):
     print('[class attributes]')
     for attribute in self.__dict__.keys():
       if attribute[:2] != '__':
         value = getattr(self, attribute)
         if not callable(value):
           print(attribute, '=', value)

v = MyClass(4,2)
v.print_class_attributes()
v.print_instance_attributes()

Lưu ý: print_class_attributes() nên       nhưng không phải trong ví dụ ngu ngốc và đơn giản này .@staticmethod

Kết quả cho

$ python2 ./print_attributes.py
[class attributes]
a = 12
b = 34
[instance attributes]
c = 4
d = 2

Kết quả tương tự cho

$ python3 ./print_attributes.py
[class attributes]
b = 34
a = 12
[instance attributes]
c = 4
d = 2

8

MyClass().__class__.__dict__

Tuy nhiên, "quyền" được thực hiện là thông qua mô-đun kiểm tra .


6
MyClass().__class__.__dict__==MyClass.__dict__
yak

5
Nhận xét của @ yak không hoàn toàn đúng. Xem phần sau về sự khác biệt giữa các thuộc tính lớp và cá thể. Xem stackoverflow.com/questions/35805/… .
sholsapp

@sholsapp thực sự @yak đã đúng. Các liên kết mà bạn cung cấp nói rằng MyClass().__class__.__dict__ != MyClass().__dict__, nhưng yak là chưa kể các dấu ngoặc đơn ở phía bên tay phải, trong trường hợp mà s / anh là đúng
Shadi

2
import re

class MyClass:
    a = "12"
    b = "34"

    def myfunc(self):
        return self.a

attributes = [a for a, v in MyClass.__dict__.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]

Đối với một ví dụ của MyClass, chẳng hạn như

mc = MyClass()

sử dụng type(mc)thay thế MyClasstrong danh sách hiểu. Tuy nhiên, nếu người ta thêm động một thuộc tính vào mc, chẳng hạn như mc.c = "42", thuộc tính đó sẽ không hiển thị khi sử dụng type(mc)trong chiến lược này. Nó chỉ đưa ra các thuộc tính của lớp gốc.

Để có được từ điển hoàn chỉnh cho một phiên bản lớp, bạn cần KẾT HỢP các từ điển của type(mc).__dict__mc.__dict__.

mc = MyClass()
mc.c = "42"

# Python 3.5
combined_dict = {**type(mc).__dict__, **mc.__dict__}

# Or Python < 3.5
def dict_union(d1, d2):
    z = d1.copy()
    z.update(d2)
    return z

combined_dict = dict_union(type(mc).__dict__, mc.__dict__)

attributes = [a for a, v in combined_dict.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]

Giải pháp thực sự gọn gàng.
Gitnik

2

Tôi không biết liệu có thứ gì đó tương tự đã được tạo ra từ bây giờ hay không, nhưng tôi đã tạo một hàm tìm kiếm thuộc tính tuyệt vời bằng cách sử dụng vars (). vars () tạo từ điển các thuộc tính của một lớp mà bạn chuyển qua nó.

class Player():
    def __init__(self):
        self.name = 'Bob'
        self.age = 36
        self.gender = 'Male'

s = vars(Player())
#From this point if you want to print all the attributes, just do print(s)

#If the class has a lot of attributes and you want to be able to pick 1 to see
#run this function
def play():
    ask = input("What Attribute?>: ")
    for key, value in s.items():
        if key == ask:
            print("self.{} = {}".format(key, value))
            break
    else:
        print("Couldn't find an attribute for self.{}".format(ask))

Tôi đang phát triển một Text Adventure khá lớn bằng Python, lớp Người chơi của tôi cho đến nay có hơn 100 thuộc tính. Tôi sử dụng cái này để tìm kiếm các thuộc tính cụ thể mà tôi cần xem.


may VAR () sẽ không trở lại các thuộc tính lớp
user2682863

Bạn đã thử chạy mã tôi đã đăng chưa? Vars chắc chắn có thể trả về các thuộc tính của lớp. Chỉ cho tôi một ví dụ về cách nó không? Có thể mã của tôi không chính xác. Nhưng việc gán vars () cho một biến và sử dụng khóa, tìm kiếm giá trị thông qua biến đó có thể trả về các thuộc tính của lớp.
Corey Bailey

lớp T: x = 1; t = T (); vars (t)
user2682863

Bạn sẽ phải đợi cho đến khi tôi tan sở để chỉ cho bạn một cách chính xác. Nhưng mã của bạn không chính xác. Đối tượng lớp của bạn cần xác định __init __ (self) và x cần là self.x = 1. Sau đó gán t = T () và sử dụng print (vars (t)) và nó sẽ hiển thị cho bạn một từ điển của tất cả các thuộc tính của lớp.
Corey Bailey

không, đó là các thuộc tính cá thể không phải thuộc tính lớp, nhiều lớp con không bao giờ gọi init . Như tôi đã nói, vars () sẽ không trở lại các thuộc tính lớp, bạn cần phải sử dụng dir () hoặc inspect.getmembers ()
user2682863

2

Điều này có thể được thực hiện mà không cần thanh tra, tôi đoán.

Tham gia lớp học sau:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Nhìn vào các thành viên cùng với các loại của họ:

t = Test()
l = [ (x, eval('type(x.%s).__name__' % x)) for x in dir(a) ]

... cho:

[('__doc__', 'NoneType'),
 ('__init__', 'instancemethod'),
 ('__module__', 'str'),
 ('a', 'int'),
 ('b', 'int'),
 ('c', 'int'),
 ('test', 'instancemethod'),
 ('toto', 'function')]

Vì vậy, để chỉ xuất các biến, bạn chỉ cần lọc kết quả theo loại và tên không bắt đầu bằng '__'. Ví dụ

filter(lambda x: x[1] not in ['instancemethod', 'function'] and not x[0].startswith('__'), l)

[('a', 'int'), ('b', 'int'), ('c', 'int')] # actual result

Đó là nó.

Lưu ý: nếu bạn đang sử dụng Python 3, hãy chuyển đổi các trình vòng lặp thành danh sách.

Nếu bạn muốn một cách hiệu quả hơn, hãy sử dụng thanh tra .


2

Python 2 & 3, cho đến khi nhập, lọc các đối tượng theo địa chỉ của chúng

Các giải pháp trong ngắn hạn:

Trả về dict {property_name: thuộc tính_value} , các đối tượng được lọc. I E{'a': 1, 'b': (2, 2), 'c': [3, 3]}

{k: val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)}

Trả về danh sách [tên_tính_tính] , các đối tượng được lọc. I E['a', 'b', 'c', 'd']

[k for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)]

Trả về danh sách [giá_trị_ thuộc tính] , các đối tượng được lọc. I E[1, (2, 2), [3, 3], {4: 4}]

[val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)]

Không lọc đối tượng

Loại bỏ ifđiều kiện. Trở về{'a': 1, 'c': [3, 3], 'b': (2, 2), 'e': <function <lambda> at 0x7fc8a870fd70>, 'd': {4: 4}, 'f': <object object at 0x7fc8abe130e0>}

{k: val for k, val in self.__dict__.items()}

Giải pháp lâu dài

Miễn là việc triển khai mặc định của __repr__không bị ghi đè , ifcâu lệnh sẽ trả về Truenếu biểu diễn thập lục phân của vị trí trong bộ nhớ valnằm trong __repr__chuỗi trả về.

Về việc triển khai mặc định của __repr__bạn, bạn có thể tìm thấy câu trả lời hữu ích này . Nói ngắn gọn:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

Wich trả về một chuỗi như:

<__main__.Bar object at 0x7f3373be5998>

Vị trí trong bộ nhớ của mỗi phần tử được lấy thông qua id()phương thức.

Tài liệu Python nói về id ():

Trả lại "danh tính" của một đối tượng. Đây là một số nguyên được đảm bảo là duy nhất và không đổi cho đối tượng này trong suốt thời gian tồn tại của nó. Hai đối tượng có vòng đời không trùng lặp có thể có cùng giá trị id ().

Chi tiết triển khai CPython: Đây là địa chỉ của đối tượng trong bộ nhớ.


Hãy thử một mình

class Bar:

    def __init__(self):

        self.a = 1
        self.b = (2, 2)
        self.c = [3, 3]
        self.d = {4: 4}
        self.e = lambda: "5"
        self.f = object()

    #__str__ or __repr__ as you prefer
    def __str__(self):
        return "{}".format(

            # Solution in Short Number 1
            {k: val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)}

        )

# Main
print(Bar())

Đầu ra:

{'a': 1, 'c': [3, 3], 'b': (2, 2), 'd': {4: 4}}

Ghi chú :

  • Đã thử nghiệm với Python 2.7.13và Python3.5.3

  • Trong Python, 2.x .iteritems()được ưu tiên hơn.items()


1

Gần đây, tôi cần tìm ra điều gì đó tương tự như câu hỏi này, vì vậy tôi muốn đăng một số thông tin cơ bản có thể hữu ích cho những người khác gặp phải vấn đề tương tự trong tương lai.

Đây là cách nó hoạt động trong Python (từ https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClasslà một đối tượng lớp, MyClass()là một thể hiện của đối tượng lớp. Một cá thể __dict__chỉ giữ các thuộc tính và phương thức cụ thể cho cá thể đó (ví dụ self.somethings). Nếu một thuộc tính hoặc phương thức là một phần của một lớp, thì nó nằm trong lớp đó __dict__. Khi bạn làm như vậy MyClass().__dict__, một phiên bản của MyClassđược tạo không có thuộc tính hoặc phương thức nào ngoài các thuộc tính lớp, do đó__dict__

Vì vậy, nếu bạn nói print(MyClass().b), trước tiên Python sẽ kiểm tra dict của phiên bản mới MyClass().__dict__['b']và không tìm thấy b. Sau đó, nó kiểm tra lớp MyClass.__dict__['b']và tìm b.

Đó là lý do tại sao bạn cần inspectmô-đun, để mô phỏng quá trình tìm kiếm tương tự.


2
Scott - Một bình luận được đăng dưới dạng câu trả lời phải bị xóa, nếu không chúng ta sẽ chết chìm trong chúng. Tuy nhiên, một câu trả lời một phần hoặc "thúc đẩy hữu ích" đối với một giải pháp vẫn là một câu trả lời . Bạn sẽ thấy cách tôi đặt lại từ khóa cho bài đăng của bạn; hy vọng tôi vẫn giữ ý định của bạn. Nếu không, bạn có thể chỉnh sửa thêm nó thành hình dạng. Chúc mừng!
Mogsdad

1

Bạn có thể sử dụng dir()trong phần hiểu danh sách để lấy tên thuộc tính:

names = [p for p in dir(myobj) if not p.startswith('_')]

Sử dụng getattr()để nhận các thuộc tính:

attrs = [getattr(myobj, p) for p in dir(myobj) if not p.startswith('_')]

1

Giải pháp của tôi để lấy tất cả các thuộc tính (không phải phương thức) của một lớp (nếu lớp đó có chuỗi tài liệu được viết đúng cách có các thuộc tính được viết rõ ràng):

def get_class_attrs(cls):
    return re.findall(r'\w+(?=[,\)])', cls.__dict__['__doc__'])

Phần này cls.__dict__['__doc__']trích xuất chuỗi tài liệu của lớp.


1

Tại sao bạn cần liệt kê các thuộc tính? Có vẻ như về mặt ngữ nghĩa lớp học của bạn là một tập hợp. Trong trường hợp này, tôi khuyên bạn nên sử dụng enum:

import enum

class myClass(enum.Enum):
     a = "12"
     b = "34"

Liệt kê các thuộc tính của bạn? Không có gì dễ dàng hơn thế này:

for attr in myClass:
    print("Name / Value:", attr.name, attr.value)

1

Nếu bạn muốn "lấy" một thuộc tính, có một câu trả lời rất đơn giản , rõ ràng là: getattr

class MyClass(object):
a = '12'
b = '34'
def myfunc(self):
    return self.a

>>> getattr(MyClass, 'a')
'12'

>>> getattr(MyClass, 'myfunc')
<function MyClass.myfunc at 0x10de45378>

Nó hoạt động tốt trên cả Python 2.7 và Python 3.x.

Nếu bạn muốn có một danh sách các mục này, bạn vẫn cần sử dụng thanh tra.


1
Câu trả lời đó có quá đơn giản và quá chính xác để xứng đáng với bất kỳ điểm nào, và thậm chí nên đáng bị điểm xấu? Có vẻ như ngày nay nền kinh tế và sự đơn giản không còn được đền đáp nữa.
fralau

0

hai chức năng:

def get_class_attr(Cls) -> []:
    import re
    return [a for a, v in Cls.__dict__.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]

def get_class_attr_val(cls):
    attr = get_class_attr(type(cls))
    attr_dict = {}
    for a in attr:
        attr_dict[a] = getattr(cls, a)
    return attr_dict

sử dụng:

>>> class MyClass:
    a = "12"
    b = "34"
    def myfunc(self):
        return self.a

>>> m = MyClass()
>>> get_class_attr_val(m)
{'a': '12', 'b': '34'}

0

Sau đây là những gì tôi muốn.

Dữ liệu thử nghiệm

class Base:
    b = 'b'


class MyClass(Base):
    a = '12'

    def __init__(self, name):
        self.name = name

    @classmethod
    def c(cls):
        ...

    @property
    def p(self):
        return self.a

    def my_fun(self):
        return self.name
print([name for name, val in inspect.getmembers(MyClass) if not name.startswith('_') and not callable(val)])  # need `import inspect`
print([_ for _ in dir(MyClass) if not _.startswith('_') and not callable(getattr(MyClass, _))])
# both are equ: ['a', 'b', 'p']

my_instance = MyClass('c')
print([_ for _ in dir(my_instance) if not _.startswith('_') and not callable(getattr(my_instance, _))])
# ['a', 'b', 'name', 'p']

-2

Tôi biết điều này là ba năm trước, nhưng đối với những người sẽ gặp câu hỏi này trong tương lai, đối với tôi:

class_name.attribute 

hoạt động tốt.


3
ngoại trừ khi bạn gặp lỗi AttributeError.
rady

Không phải lúc nào bạn cũng biết attributetrước được điều gì .
Matt Luongo,

-3

Bạn có thể sử dụng MyClass.__attrs__. Nó chỉ đưa ra tất cả các thuộc tính của lớp đó. Chỉ có bấy nhiêu thôi.


AttributeError: loại đối tượng 'X' không có thuộc tính ' attrs '
Ramazan Polat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.