Làm thế nào tôi có thể tạo một đối tượng và thêm thuộc tính cho nó?


311

Tôi muốn tạo một đối tượng động (bên trong một đối tượng khác) trong Python và sau đó thêm các thuộc tính cho nó.

Tôi đã thử:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

nhưng điều này đã không làm việc.

Có ý kiến ​​gì không?

biên tập:

Tôi đang thiết lập các thuộc tính từ một forvòng lặp lặp qua danh sách các giá trị, vd

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

Trong ví dụ trên, chúng tôi sẽ nhận được obj.a.attr1, obj.a.attr2, obj.a.attr3.

Tôi đã sử dụng setattrhàm vì tôi không biết cách thực hiện obj.a.NAMEtừ một forvòng lặp.

Làm cách nào để đặt thuộc tính dựa trên giá trị ptrong ví dụ trên?


4
"Không làm việc" nghĩa là gì? Tôi giả sử nó đã đưa ra một ngoại lệ AttributionError, phải không?
Josh Wright

1
vâng đối tượng 'object' không có thuộc tính 'somefield'
John

6
Tại sao anh làm điều này? Một "đối tượng" chung chung không có ý nghĩa thực sự . Ý nghĩa của thứ bạn đang tạo ra là gì? Tại sao nó không phải là một lớp thích hợp hoặc được đặt tên?
S.Lott

1
Ví dụ này không tối thiểu và khó hiểu đối với tôi hoặc tôi chỉ không hiểu tại sao bạn không làm việc với một số người a = object()và bạn cần obj.a = object(). Một lần nữa tôi đang nói về ví dụ, trong mã thực tế của bạn, một đối tượng bên trong một đối tượng có thể hữu ích.
kon tâm lý

Câu trả lời:


215

Bạn có thể sử dụng công thức Bunch cổ xưa của tôi , nhưng nếu bạn không muốn tạo một "lớp bó", một thứ rất đơn giản đã tồn tại trong Python - tất cả các hàm có thể có các thuộc tính tùy ý (bao gồm các hàm lambda). Vì vậy, các công việc sau đây:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

Cho dù mất sự rõ ràng so với Bunchcông thức đáng kính là OK, đó là một quyết định phong cách tất nhiên tôi sẽ để lại cho bạn.


25
@FogleBird, một quyết định phong cách, như tôi đã đề cập. Một số chuyên gia CS được đào tạo, ví dụ như tính toán lambda của Church được sử dụng để nghĩ các hàm (lambdas) là loại cơ bản của tất cả dữ liệu (ví dụ, số nguyên 23, có thể được xem là tương đương lambda: 23), vì vậy các chuyên gia như vậy sử dụng lambdas cho mục đích này có lẽ sẽ cảm thấy không có gì giống như "một hack". Cá nhân tôi không thích lambda Python lắm - nhưng đó là vấn đề của sở thích cá nhân.
Alex Martelli

Và trong một số trường hợp, việc xem xét liệu lambdamô hình có phù hợp với trường hợp sử dụng của bạn hay không có thể khiến bạn nhận ra rằng một cái gì đó ban đầu bạn nghĩ là dữ liệu thực sự giống một chức năng hơn - hoặc, trong mọi trường hợp, một functor.
Kyle Strand

5
@ naught101, một hàm một đối tượng, trong Python, vì vậy sự phản đối của bạn là không thể đo lường được.
Alex Martelli

6
@ naught101, tránh việc tạo ra một loại mới (tái sử dụng một loại hiện có) không phức tạp, nó đơn giản hóa. Ngày nay, trên thực tế có thể thích from argparse import Namespacemặc dù tôi muốn nó sống ở nơi khác * ví dụ collection:) - sử dụng lại một loại hiện có, chỉ là loại tốt hơn và vẫn tránh tạo kiểu mới. Nhưng, nó đã không ở đó sau đó :-).
Alex Martelli

1
Xem câu trả lời bên dưới từ "JF Sebastian" về SimpleNamespace từ mô-đun loại. Nếu phiên bản python của bạn hỗ trợ nó, đây là giải pháp tốt nhất (và chính xác là SimpleNamespace được thiết kế cho)
Tim Richardson

334

Tích hợp objectcó thể được khởi tạo nhưng không thể có bất kỳ thuộc tính nào được đặt trên nó. (Tôi ước nó có thể, với mục đích chính xác này.) Nó không phải __dict__giữ các thuộc tính.

Tôi thường chỉ làm điều này:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

Khi tôi có thể, tôi đặt cho Objectlớp một cái tên có ý nghĩa hơn, tùy thuộc vào loại dữ liệu tôi đặt vào đó.

Một số người làm một việc khác, trong đó họ sử dụng một lớp dictcon cho phép truy cập thuộc tính để lấy khóa. ( d.keythay vì d['key'])

Chỉnh sửa : Để thêm vào câu hỏi của bạn, sử dụng setattrlà tốt. Bạn không thể sử dụng setattrtrong các object()trường hợp.

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)

9
có thể được khởi tạo, chỉ không được sử dụng cho bất cứ điều gì hữu ích một khi nó đã được thực hiện. foo = object()hoạt động, nhưng bạn không thể làm được gì nhiều với nó
Daniel DiPaolo

Chào. Cảm ơn câu trả lời. Tôi đã cập nhật vấn đề của tôi ở trên. xem chỉnh sửa. Bạn có biết câu trả lời cho điều này?
Giăng

xin lỗi tôi vẫn muốn đặt nó trên đối tượng xem cập nhật ở trên.
Giăng

Tôi thực sự thích câu trả lời của bạn và tôi nghĩ rằng tôi sẽ nghiêng về hướng này trong tương lai. Tôi đã sử dụng về mọi thứ khác trên bài đăng này ngoại trừ phương pháp rất đơn giản, dễ hiểu và dễ đọc này. Sử dụng type....hoặc lambda chưa bao giờ là fav của tôi, giống như văn bản nôn trong mã của tôi. Nhưng ý tưởng này là tuyệt vời để sử dụng các đối tượng để giữ các thuộc tính. Để lại mã dễ đọc hơn b / c khi tôi thấy lambda Tôi giảm tốc độ đọc xuống 25% trong khi cách của bạn hoàn toàn có ý nghĩa! Cảm ơn.
Marc

Câu trả lời tuyệt vời, điều duy nhất tôi thay đổi là sử dụng Structlàm tên của lớp để làm cho nó rõ ràng hơn. Tiết kiệm cho tôi một tấn đánh máy [""], Chúc mừng!
pragman

137

types.SimpleNamespacelớp trong Python 3.3+ :

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtuple, typing.NamedTuplecó thể được sử dụng cho các đối tượng bất biến. PEP 557 - Các lớp dữ liệu gợi ý một sự thay thế có thể thay đổi.

Để có chức năng phong phú hơn, bạn có thể thử attrsgói . Xem một ví dụ sử dụng .


3
Nếu bạn cần thứ gì đó hoạt động với Python 2.7, bạn cũng có thể thử argparse.Namespacelớp
RolKau

Đồng ý - Tôi sẽ tò mò nếu có một nhược điểm ở đây, nhưng đây là khả năng chi trả vô cùng tiện dụng của con trăn 3.3+.
ghukill

chỉ trích! Điều này không có sẵn trên 2.7?
Roel

attrsGói @Roel hỗ trợ Python 2.7
jfs

Đây có vẻ là một giải pháp tốt hơn cho tôi so với unittest.mock; cái sau thì hơi nặng và dễ uốn hơn một chút. Với một đối tượng giả, chỉ cần gán cho một thuộc tính sẽ khiến nó xuất hiện; SimpleNamespace sẽ chống lại điều đó.
jdzions

32

Có một vài cách để đạt được mục tiêu này. Về cơ bản bạn cần một đối tượng có thể mở rộng.

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()

14
obj.a = type('', (), {})
iman

27

Các mockmô-đun về cơ bản được thực hiện cho điều đó.

import mock
obj = mock.Mock()
obj.a = 5

3
Nhược điểm là sự phụ thuộc bên ngoài
Kangur

6
unittest.Mocklà một phần của thư viện tiêu chuẩn kể từ Python 3.3 ( docs.python.org/3/library/unittest.mock.html )
illagrenan

2
Tôi nghĩ phụ thuộc vào việc sử dụng mã của bạn. Nếu đó là mã sản xuất, tôi sẽ không muốn một số mocktrong đó. Chỉ cảm thấy kỳ lạ với tôi.
Mike de Klerk

21

Bây giờ bạn có thể làm (không chắc đó có phải là câu trả lời giống như evilpie không):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42

Câu trả lời của @ evilpie đặt các thuộc tính trực tiếp trên MyObject (lớp), chứ không phải ví dụ như của bạn.
JFS

18

Hãy thử mã dưới đây:

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']

1
Bạn có thể giải thích thêm về những gì nó làm? trong khi mã có thể hữu ích để giải quyết vấn đề này, việc giải thích nó có thể đi xa hơn nhiều so với chỉ một vấn đề.
DeadChex

1
@DeadChex Rõ ràng nó tạo ra một Class (đối tượng) mới là một lớp trống với các thuộc tính đối tượng và lưu trữ các thuộc tính bên trong lớp. Điều này thậm chí còn tốt hơn so với cài đặt nhiều mô-đun, hoặc dựa vào lambdas.
m3nda

2
Không chắc chắn tại sao điều này không có nhiều upvote. Có một lý do để không sử dụng điều này cho một lớp container cơ bản? Có vẻ hoạt động tốt trong Python 2.7, 2.6 và 3.4
user5359531

17

Bạn cũng có thể sử dụng một đối tượng lớp trực tiếp; nó tạo ra một không gian tên:

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')

9

như các tài liệu nói :

Lưu ý : objectkhông không__dict__, vì vậy bạn có thể không thuộc tính tùy ý assign để một thể hiện của objectlớp.

Bạn chỉ có thể sử dụng ví dụ lớp giả.


2

Những giải pháp này rất hữu ích trong quá trình thử nghiệm. Dựa trên câu trả lời của mọi người khác, tôi thực hiện điều này trong Python 2.7.9 (không có tĩnh điện, tôi nhận được TypeError (phương thức không liên kết ...):

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4

1

Những đối tượng bạn đang sử dụng? Chỉ cần thử điều đó với một lớp mẫu và nó hoạt động tốt:

class MyClass:
  i = 123456
  def f(self):
    return "hello world"

b = MyClass()
b.c = MyClass()
setattr(b.c, 'test', 123)
b.c.test

Và tôi đã nhận được 123câu trả lời.

Tình huống duy nhất mà tôi thấy thất bại này là nếu bạn đang thử một setattrđối tượng dựng sẵn.

Cập nhật: Từ nhận xét, đây là sự lặp lại của: Tại sao bạn không thể thêm thuộc tính vào đối tượng trong python?


bc được đặt thành object () không phải là một lớp được xác định
John

0

Đến đây vào cuối ngày nhưng đây là đồng xu của tôi với một đối tượng tình cờ nắm giữ một số đường dẫn hữu ích trong một ứng dụng nhưng bạn có thể điều chỉnh nó cho bất cứ nơi nào bạn muốn có một loại thông tin mà bạn có thể truy cập bằng getattr và ký hiệu chấm (đó là những gì tôi nghĩ câu hỏi này thực sự là về):

import os

def x_path(path_name):
    return getattr(x_path, path_name)

x_path.root = '/home/x'
for name in ['repository', 'caches', 'projects']:
    setattr(x_path, name, os.path.join(x_path.root, name))

Điều này thật tuyệt vì bây giờ:

In [1]: x_path.projects
Out[1]: '/home/x/projects'

In [2]: x_path('caches')
Out[2]: '/home/x/caches'

Vì vậy, điều này sử dụng đối tượng hàm như các câu trả lời ở trên nhưng sử dụng hàm để lấy các giá trị (bạn vẫn có thể sử dụng (getattr, x_path, 'repository')thay vì x_path('repository')nếu bạn thích).


0

Nếu chúng ta có thể xác định và tổng hợp tất cả các thuộc tính và giá trị lại với nhau trước khi tạo đối tượng lồng nhau, thì chúng ta có thể tạo một lớp mới có một đối số từ điển khi tạo.

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

Chúng tôi cũng có thể cho phép các đối số từ khóa. Xem bài này .

class NestedObject(object):
    def __init__(self, *initial_attrs, **kwargs):
        for dictionary in initial_attrs:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')

-2
di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")

-2

Cách khác tôi thấy, cách này:

import maya.cmds

def getData(objets=None, attrs=None):
    di = {}
    for obj in objets:
        name = str(obj)
        di[name]=[]
        for at in attrs:
            di[name].append(cmds.getAttr(name+'.'+at)[0])
    return di

acns=cmds.ls('L_vest_*_',type='aimConstraint')
attrs=['offset','aimVector','upVector','worldUpVector']

getData(acns,attrs)

bạn có thể thêm vào thêm tên attr this di [name] .append ([at, cmds.getAttr (name + '.' + at) [0]])
Pablo Emmanuel De Leo

1
Điều này là thêm một phụ thuộc phi tiêu chuẩn rất lớn trong khi đơn giản class a: passcung cấp tất cả sức mạnh cần thiết.
Alexis Paques
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.