2010-02-17 71 views
22

Này, tôi mới bắt đầu băn khoăn về điều này khi tôi xuất hiện một mã dự kiến ​​một đối tượng với một tập hợp các thuộc tính nhất định (nhưng không có đặc tả nào về loại đối tượng này).Cách ngắn nhất để tạo một đối tượng với các thuộc tính tùy ý trong Python?

Một giải pháp là tạo một lớp mới có các thuộc tính mà mã mong đợi, nhưng khi tôi gọi mã khác cũng cần các đối tượng có thuộc tính (khác), tôi phải tạo nhiều lớp hơn.

Một giải pháp ngắn hơn là tạo một lớp chung, và sau đó đặt thuộc tính về trường hợp của nó (đối với những người nghĩ đến việc sử dụng thể hiện object thay vì tạo lớp mới, sẽ không hoạt động kể từ object trường hợp không không cho phép các thuộc tính mới).

Các giải pháp cuối cùng, ngắn nhất tôi đã đưa ra là tạo ra một lớp với một constructor mà mất đối số từ khóa, giống như các nhà xây dựng dict, và sau đó đặt chúng như thuộc tính:

class data: 
    def __init__(self, **kw): 
     for name in kw: 
      setattr(self, name, kw[name]) 

options = data(do_good_stuff=True, do_bad_stuff=False) 

Nhưng tôi có thể' t giúp cảm thấy như tôi đã bỏ lỡ một cái gì đó hiển nhiên ... Không có một cách tích hợp để làm điều này (tốt nhất là hỗ trợ trong Python 2.5)?

+4

Mọi người sử dụng Python (ngoại trừ một số goofballs viết stdlib) tên lớp học của họ với các từ viết hoa, giúp mọi người nhận ra tên nào đại diện. Ngoài ra, đó là một thói quen tốt để luôn kế thừa từ 'đối tượng', do đó bạn đang sử dụng * các lớp kiểu mới *. –

+0

Vâng, tôi muốn lớp trông giống như một kiểu dữ liệu vì nó không có chức năng lớp (đó cũng là lý do tôi cố ý không sử dụng các lớp kiểu mới vì tôi không sử dụng cho thứ tự phân giải phương thức, v.v.) có lẽ nên có tên viết hoa, bạn nói đúng. – Blixt

Trả lời

18

Mã ban đầu có thể được sắp xếp hợp lý một chút bằng cách sử dụng __dict__:

In [1]: class data: 
    ...:  def __init__(self, **kwargs): 
    ...:   self.__dict__.update(kwargs) 
    ...: 

In [2]: d = data(foo=1, bar=2) 

In [3]: d.foo 
Out[3]: 1 

In [4]: d.bar 
Out[4]: 2 
+0

Tôi nghĩ rằng giải pháp này có thể giải quyết các mối quan tâm Blixt đã có với câu trả lời của S.Lott. – cmcginty

16

Sử dụng collections.namedtuple.

Nó hoạt động tốt.

from collections import namedtuple 
Data = namedtuple('Data', [ 'do_good_stuff', 'do_bad_stuff' ]) 
options = Data(True, False) 
+1

Tốt, mặc dù bổ sung rất nhiều chức năng không cần thiết trong trường hợp của tôi. Thật không may, hầu hết mã tôi viết được giới hạn trong Python 2.5 và 'namedtuple' được thêm vào 2.6. +1 mặc dù =) – Blixt

+0

@Blixt: "chức năng không cần thiết"? Vậy cái gì? Hầu hết Python có thể được gọi là chức năng không cần thiết cho các trường hợp sử dụng. Xem xét nâng cấp. Không có gì phá vỡ; bạn nhận được câu lệnh 'with' và khả năng sử dụng hàm' print'. –

+1

Tôi chỉ có nghĩa là tôi có thể đi với một giải pháp đơn giản hơn, giải pháp này có thêm chức năng tuple mà tôi không thực sự cần (cộng thêm nó tạo ra một lớp hoàn toàn mới, tôi chỉ quan tâm đến việc tạo một cá thể duy nhất với các thuộc tính được chỉ định mà tôi sau đó có thể vứt đi). Tôi không thể xem xét nâng cấp vì đây là một dự án tại nơi làm việc, có quy định nghiêm ngặt về phần mềm được sử dụng. – Blixt

0

Nếu tôi hiểu câu hỏi của bạn chính xác, bạn cần bản ghi. Các lớp Python có thể được sử dụng theo cách này, đó là những gì bạn làm.

Tôi tin rằng cách thức đa chiều nhất để đối phó với "hồ sơ" chỉ đơn giản là ... từ điển! Một lớp là một loại từ điển về steroid.

Ví dụ về lớp học data về bản chất là cách chuyển đổi từ điển sang lớp học.

(Trên một mặt lưu ý, tôi thà sử dụng self.__setattr__(name, kw[name]).)

+1

Tuy nhiên, bạn không thể sử dụng 'd = {'field': 123}' như 'd.field', tôi nghĩ đó là ý định của anh ta. –

+0

Chính xác những gì Dejw đã nói. Tôi cũng muốn sử dụng 'dict' trong trường hợp cụ thể này. Tôi nghĩ rằng bằng cách sử dụng phương pháp '__setattr__' trong trường hợp này là vô nghĩa, bởi vì nó tương đương với' setattr', nhưng dài hơn, và không thêm bất kỳ sự rõ ràng cho những gì đang xảy ra. – Blixt

+1

Olivier, Sử dụng '__setattr_ trong trường hợp này không hoạt động, cũng không tốt hơn nếu nó đã làm. –

9

Đây là con đường ngắn nhất tôi biết

>>> obj = type("myobj",(object,),dict(foo=1,bar=2)) 
>>> obj.foo 
1 
>>> obj.bar 
2 
>>> 

sử dụng dict thay vì {} đảm bảo tên thuộc tính của bạn có giá trị

>>> obj = type("myobj",(object,),{"foo-attr":1,"bar-attr":2}) 
>>> obj.foo-attr 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: type object 'myobj' has no attribute 'foo' 
>>> 
+1

Giải pháp thú vị! Nhưng những gì nó thực sự làm là tạo ra một kiểu mới (có thể được khởi tạo, tức là, 'o = obj()'). Đối với ví dụ thứ hai của bạn, bạn * có thể * làm 'getattr (obj, 'foo-attr')', nhưng tôi đồng ý rằng bạn nên tránh các loại tên đó cho các thuộc tính. – Blixt

+1

có, thực tế là bạn có thể nhanh chóng chắc chắn là một tác dụng phụ lạ. Tôi cũng đã chạy vào các vấn đề với việc sử dụng dicts được deserialized từ json có chuỗi unicode như các phím đôi khi rối tung lên các máy móc getattr. Nó không phải là chống đạn, và tôi sẽ không sử dụng nó ở mọi nơi, nhưng nó có thể rất tiện dụng trong những tình huống nhất định. –

0

này thường là một cái gì đó bạn sẽ sử dụng một dict cho, chứ không phải làm cho một lớp học ở tất cả.

+0

Tôi đồng ý. Tôi đang làm việc với mã ngoài tầm kiểm soát của mình. – Blixt

13

này hoạt động trong 2.5, 2.6, và 3.1:

class Struct(object): 
    pass 

something = Struct() 
something.awesome = abs 

result = something.awesome(-42) 

EDIT: Tôi nghĩ có lẽ đem lại nguồn sẽ giúp đỡ là tốt. http://docs.python.org/tutorial/classes.html#odds-and-ends

CHỈNH SỬA: Đã thêm bài tập vào kết quả, vì tôi đang sử dụng trình thông dịch tương tác để xác minh và có thể bạn không sử dụng.

6

Sử dụng một sự kết hợp giữa lambda và kiểu build-in, tôi nghĩ là cách nhỏ nhất để làm điều đó:

obj = lambda **kwargs: type('obj', (object,), kwargs)() 

options = obj(do_good_stuff=True, do_bad_stuff=False) 

print options.do_good_stuff 
print options.do_bad_stuff 
2

Nếu bạn không cần truyền các giá trị trong hàm tạo, bạn có thể thực hiện điều này:

class data: pass 

data.foo = 1 
data.bar = 2 

Bạn sử dụng các biến thành viên tĩnh lớp để giữ dữ liệu của bạn.

14

type('',(), {})() sẽ tạo một đối tượng có thể có thuộc tính tùy ý.

Ví dụ:

obj = type('',(), {})() 
obj.hello = "hello" 
obj.world = "world" 
print obj.hello, obj.world #will print "hello world" 

type() với ba đối số tạo ra một loại mới.

Đối số đầu tiên '' là tên của loại mới. Chúng tôi không quan tâm đến tên, vì vậy chúng tôi để trống.

Đối số thứ hai () là một bộ các loại cơ sở, tại đây object (ẩn).

Đối số thứ ba là một cuốn từ điển các thuộc tính của đối tượng mới - một lần nữa chúng tôi không quan tâm đến nó là một từ điển rỗng {}

Và cuối cùng chúng tôi nhanh chóng một trường hợp mới của loại mới này với () tại kết thúc.

4

Một hàm là một đối tượng. Vì vậy, bạn có thể gán các thuộc tính cho một hàm. Hoặc tạo một cái. Đây là cách đơn giản nhất về dòng mã, tôi nghĩ vậy.

def hello(): 
    pass 


hello.chook = 123 

nhưng & cách thanh lịch nhất đơn giản nhất (nhưng python> 3.3) là sử dụng simpleNamespace Chuẩn THƯ VIỆN của https://docs.python.org/3/library/types.html#types.SimpleNamespace

>>> from types import SimpleNamespace 
>>> foo = SimpleNamespace() 
>>> foo.hello = "world" 
Các vấn đề liên quan