Làm thế nào để lấy các biến phiên bản trong Python?


120

Có phương thức tích hợp sẵn trong Python để lấy một mảng tất cả các biến phiên bản của một lớp không? Ví dụ: nếu tôi có mã này:

class hi:
  def __init__(self):
    self.ii = "foo"
    self.kk = "bar"

Có cách nào để tôi làm điều này:

>>> mystery_method(hi)
["ii", "kk"]

Chỉnh sửa: Ban đầu tôi đã yêu cầu sai các biến lớp.


Ví dụ của bạn hiển thị "biến phiên bản". Biến lớp là một thứ khác.
S.Lott 20-08

Câu trả lời:


143

Mọi đối tượng đều có một __dict__biến chứa tất cả các biến và giá trị của nó trong đó.

Thử cái này

>>> hi_obj = hi()
>>> hi_obj.__dict__.keys()

7
FWIW, mô-đun kiểm tra cung cấp cho bạn một phương pháp đáng tin cậy hơn là dựa vào dict , điều mà không phải tất cả các phiên bản đều có.
Thomas Wouters 20-08

1
Loại ví dụ nào không có dict ? Tôi chưa bao giờ gặp phải.
Carl Meyer 20-08

3
Một số kiểu tích hợp sẵn, chẳng hạn như int. Hãy thử nói "x = 5" rồi đến "x .__ dict__" và bạn sẽ nhận được AttributeError
Eli Courtwright 21/09/08

6
Ngoài ra, bất cứ thứ gì sử dụng khe cắm .
Thomas Wouters

2
Tại sao bạn muốn thử và lấy các biến cá thể lớp của một int? Nó thậm chí không phải là một lớp học (mặc dù tôi có thể sai về điều đó).
Martin Sherburn

103

Sử dụng vars ()

class Foo(object):
    def __init__(self):
        self.a = 1
        self.b = 2

vars(Foo()) #==> {'a': 1, 'b': 2}
vars(Foo()).keys() #==> ['a', 'b']

6
+1 Cá nhân tôi nghĩ rằng cú pháp này rõ ràng hơn so với sử dụng dict , vì các thuộc tính có dấu gạch dưới kép được cho là "riêng tư" trong cú pháp Python.
Martin W 20-08

3
@Martin: __methodlà private, __method__là một phương thức đặc biệt, không nhất thiết phải private; Tôi muốn nói rằng các phương thức đặc biệt xác định capabilites của một đối tượng hơn là các phương thức.
u0b34a0f6ae

2
__method__khá xấu xí, và tôi nghĩ chúng đã được đặt tên như vậy để cố gắng ngăn chặn mọi người sử dụng chúng trừ khi thực sự cần thiết. Trong trường hợp này, chúng ta có vars thay thế ().
Martin Sherburn

trong trường hợp của tôi, tôi đã thử gọi vars (ObjName) và nó trả về dict_proxy với từ điển có khóa là các phương thức của lớp / đối tượng đã cho, nhưng không có dấu hiệu của phần tử init . Bất kỳ đoán tại sao?
user228137

Biến Dấu gạch dưới Đôi __str__, __method__dành cho ngôn ngữ bình thường. Điều gì xảy ra khi bạn str(something)?
Zizouz212

15

Thông thường, bạn không thể nhận các thuộc tính cá thể chỉ được cung cấp cho một lớp, ít nhất là không khởi tạo lớp đó. Tuy nhiên, bạn có thể nhận các thuộc tính cá thể cho một cá thể hoặc các thuộc tính lớp cho một lớp. Xem mô-đun 'kiểm tra'. Bạn không thể nhận danh sách các thuộc tính cá thể vì các cá thể thực sự có thể có bất kỳ thứ gì dưới dạng thuộc tính và - như trong ví dụ của bạn - cách thông thường để tạo chúng là chỉ cần gán cho chúng trong phương thức __init__.

Một ngoại lệ là nếu lớp của bạn sử dụng các vị trí, là danh sách cố định các thuộc tính mà lớp cho phép các cá thể có. Slots được giải thích trong http://www.python.org/2.2.3/descrintro.html , nhưng có nhiều cạm bẫy khác nhau với slot; chúng ảnh hưởng đến bố trí bộ nhớ, vì vậy đa kế thừa có thể có vấn đề và kế thừa nói chung cũng phải tính đến các khe.


Từ chối vì "bạn không thể lấy danh sách các thuộc tính cá thể" chỉ đơn giản là sai: cả vars () và dict đều cung cấp cho bạn chính xác điều đó.
Carl Meyer 20-08

2
Tôi giả sử ý của anh ấy là "bạn không thể lấy danh sách tất cả các thuộc tính phiên bản có thể có". Ví dụ: tôi thường sử dụng lười biếng tạo và trình trang trí @property để thêm một biến thể hiện vào lần đầu tiên nó được yêu cầu. Sử dụng dict sẽ cho bạn biết về biến này khi nó tồn tại nhưng không phải trước đó.
Eli Courtwright 21/09/08

5
Tôi không phản đối, và hãy lưu ý những gì tôi thực sự đã nói: bạn không thể nhận danh sách các thuộc tính cá thể chỉ được cung cấp cho một lớp , đó là những gì mã đi kèm câu hỏi cố gắng thực hiện.
Thomas Wouters

2
@Carl: Hãy tự giáo dục bản thân trước khi viết những nhận xét sai và phản đối dựa trên sự thiếu hiểu biết của bạn.
nikow

14

Cả hai phương thức Vars () và dict sẽ hoạt động đối với ví dụ OP đã đăng, nhưng chúng sẽ không hoạt động đối với các đối tượng được xác định "lỏng lẻo" như:

class foo:
  a = 'foo'
  b = 'bar'

Để in tất cả các thuộc tính không thể gọi, bạn có thể sử dụng hàm sau:

def printVars(object):
    for i in [v for v in dir(object) if not callable(getattr(object,v))]:
        print '\n%s:' % i
        exec('print object.%s\n\n') % i

5
exec('print object.%s\n\n') % inên được viết làprint getattr(object, i)
user102008

11

Bạn cũng có thể kiểm tra xem một đối tượng có một biến cụ thể với:

>>> hi_obj = hi()
>>> hasattr(hi_obj, "some attribute")

6

Ví dụ của bạn hiển thị "biến phiên bản", không thực sự là biến lớp.

Tìm kiếm hi_obj.__class__.__dict__.items()các biến lớp, cùng với các thành viên lớp khác như các hàm thành viên và mô-đun chứa.

class Hi( object ):
    class_var = ( 23, 'skidoo' ) # class variable
    def __init__( self ):
        self.ii = "foo" # instance variable
        self.jj = "bar"

Các biến lớp được chia sẻ bởi tất cả các trường hợp của lớp.


6

Đề xuất

>>> print vars.__doc__
vars([object]) -> dictionary

Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.

Nói cách khác, về cơ bản nó chỉ gói gọn __dict__


5

Mặc dù không trực tiếp là câu trả lời cho câu hỏi OP, nhưng có một cách khá hay để tìm ra những biến nào nằm trong phạm vi trong một hàm. hãy xem mã này:

>>> def f(x, y):
    z = x**2 + y**2
    sqrt_z = z**.5
    return sqrt_z

>>> f.func_code.co_varnames
('x', 'y', 'z', 'sqrt_z')
>>> 

Thuộc tính func_code có tất cả các loại điều thú vị trong đó. Nó cho phép bạn thực hiện một số thứ hay ho. Đây là một ví dụ về cách tôi đã sử dụng điều này:

def exec_command(self, cmd, msg, sig):

    def message(msg):
        a = self.link.process(self.link.recieved_message(msg))
        self.exec_command(*a)

    def error(msg):
        self.printer.printInfo(msg)

    def set_usrlist(msg):
        self.client.connected_users = msg

    def chatmessage(msg):
        self.printer.printInfo(msg)

    if not locals().has_key(cmd): return
    cmd = locals()[cmd]

    try:
        if 'sig' in cmd.func_code.co_varnames and \
                       'msg' in cmd.func_code.co_varnames: 
            cmd(msg, sig)
        elif 'msg' in cmd.func_code.co_varnames: 
            cmd(msg)
        else:
            cmd()
    except Exception, e:
        print '\n-----------ERROR-----------'
        print 'error: ', e
        print 'Error proccessing: ', cmd.__name__
        print 'Message: ', msg
        print 'Sig: ', sig
        print '-----------ERROR-----------\n'

2

được xây dựng dựa trên câu trả lời của dmark để có được những điều sau đây, điều này rất hữu ích nếu bạn muốn có kiến ​​thức về sprintf và hy vọng sẽ giúp được ai đó ...

def sprint(object):
    result = ''
    for i in [v for v in dir(object) if not callable(getattr(object, v)) and v[0] != '_']:
        result += '\n%s:' % i + str(getattr(object, i, ''))
    return result

0

Đôi khi bạn muốn lọc danh sách dựa trên các vars công khai / riêng tư. Ví dụ

def pub_vars(self):
    """Gives the variable names of our instance we want to expose
    """
    return [k for k in vars(self) if not k.startswith('_')]
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.