ฉันจะสร้างวัตถุและเพิ่มคุณสมบัติได้อย่างไร


310

ฉันต้องการสร้างวัตถุแบบไดนามิก (ภายในวัตถุอื่น) ใน Python แล้วเพิ่มคุณสมบัติเข้าไป

ฉันเหนื่อย:

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

แต่มันไม่ได้ผล

ความคิดใด ๆ

แก้ไข:

ฉันกำลังตั้งค่าคุณสมบัติจากforลูปซึ่งวนรอบรายการค่าเช่น

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

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

ในตัวอย่างข้างต้นฉันจะได้รับobj.a.attr1, , obj.a.attr2obj.a.attr3

ฉันใช้setattrฟังก์ชั่นนี้เพราะฉันไม่รู้วิธีการทำobj.a.NAMEจากforลูป

ฉันจะตั้งค่าแอททริบิวตามค่าของpตัวอย่างด้านบนได้อย่างไร


4
คุณหมายถึงอะไรโดย "ไม่ทำงาน" ฉันคิดว่ามันยกข้อยกเว้น AttributeError ใช่ไหม
Josh Wright

1
ใช่. 'object' object ไม่มี attribute 'somefield'
John

6
ทำไมคุณทำเช่นนี้? ทั่วไป "วัตถุ" ไม่มีจริงความหมาย อะไรคือความหมายของสิ่งที่คุณกำลังสร้าง? ทำไมมันไม่ได้เป็นชั้นเรียนที่เหมาะสมหรือตั้งชื่อ tuple?
S.Lott

1
ตัวอย่างที่ไม่ได้น้อยที่สุดและสับสนสำหรับผมหรือผมก็ไม่เห็นเหตุผลที่คุณจะไม่ได้ทำงานกับบางส่วนและคุณจำเป็นต้องa = object() obj.a = object()อีกครั้งฉันกำลังพูดถึงตัวอย่างในรหัสจริงของคุณวัตถุในวัตถุอาจมีประโยชน์
kon Psych

คำตอบ:


215

คุณสามารถใช้สูตรBunchโบราณของฉันได้แต่ถ้าคุณไม่ต้องการสร้าง "กลุ่มคลาส" สูตรที่ง่ายมากก็มีอยู่แล้วใน Python - ฟังก์ชันทั้งหมดสามารถมีคุณสมบัติตามอำเภอใจ (รวมถึงฟังก์ชั่นแลมบ์ดา) ดังนั้นงานต่อไปนี้:

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

ไม่ว่าจะสูญเสียความคมชัดเมื่อเทียบกับBunchสูตรที่น่านับถือก็โอเคคือการตัดสินใจสไตล์ฉันจะทิ้งคุณไว้แน่นอน


25
@FogleBird การตัดสินใจสไตล์ตามที่ฉันพูดถึง ผู้เชี่ยวชาญด้าน CS บางคนที่ได้รับการฝึกฝนเช่นในแลมบ์ดาของแคลคูลัสใช้ในการคิดฟังก์ชั่น (แลมบ์ดา) เป็นประเภทพื้นฐานของข้อมูลทั้งหมด (จำนวนเต็ม 23 ตัวอย่างเช่นสามารถมองเห็นได้เทียบเท่าlambda: 23) ดังนั้นผู้เชี่ยวชาญใช้lambdas เพื่อจุดประสงค์นี้ คงไม่มีอะไรเหมือน "แฮ็ค" โดยส่วนตัวแล้วฉันไม่ชอบlambda Pythonมาก - แต่นั่นเป็นเรื่องของรสนิยมส่วนตัว
Alex Martelli

และในบางกรณีการพิจารณาว่าlambdaรูปแบบเหมาะสมกับกรณีการใช้งานของคุณหรือไม่อาจทำให้คุณตระหนักได้ว่าบางสิ่งที่คุณคิดว่าเป็นข้อมูลจริง ๆ แล้วเป็นฟังก์ชั่นต่อไป - หรือไม่ว่าในกรณีใด ๆ
Kyle Strand

5
@ naught101 ฟังก์ชั่นเป็นวัตถุใน Python ดังนั้นการคัดค้านของคุณจึงไม่อาจหยั่งรู้ได้
Alex Martelli

6
@ naught101 การหลีกเลี่ยงการสร้างชนิดใหม่ (การนำชนิดที่มีอยู่กลับมาใช้ใหม่) จะไม่ซับซ้อน แต่จะทำให้ง่ายขึ้น ทุกวันนี้อาจจะชอบมากกว่าfrom argparse import Namespaceแต่ฉันหวังว่ามันจะอยู่ที่อื่น * เช่น, collection- นำประเภทที่มีอยู่ตอนนี้กลับมาใช้ใหม่ดีกว่าและยังคงหลีกเลี่ยงการสร้างชนิดใหม่ แต่มันไม่ได้อยู่ที่นั่นแล้ว :-)
Alex Martelli

1
ดูคำตอบด้านล่างจาก "JF Sebastian" เกี่ยวกับ SimpleNamespace จากโมดูลประเภท หากเวอร์ชันของไพ ธ อนของคุณรองรับนี่เป็นทางออกที่ดีที่สุด (และสิ่งที่ SimpleNamespace ได้รับการออกแบบมาเพื่อ)
Tim Richardson

334

บิวด์อินobjectสามารถอินสแตนซ์ แต่ไม่สามารถตั้งค่าคุณลักษณะใด ๆ ได้ (ฉันหวังว่ามันจะเป็นไปได้สำหรับวัตถุประสงค์ที่แน่นอนนี้) มันไม่จำเป็นต้องมี__dict__คุณสมบัติ

โดยทั่วไปฉันแค่ทำสิ่งนี้:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

เมื่อฉันสามารถฉันให้Objectชื่อมีความหมายมากขึ้นในชั้นเรียนขึ้นอยู่กับชนิดของข้อมูลที่ฉันใส่ไว้ในนั้น

บางคนทำสิ่งที่แตกต่างกันโดยที่พวกเขาใช้คลาสย่อยdictที่อนุญาตให้เข้าถึงแอตทริบิวต์เพื่อรับกุญแจ ( d.keyแทนd['key'])

แก้ไข : นอกจากคำถามของคุณแล้วการใช้setattrก็ใช้ได้ คุณไม่สามารถใช้setattrกับobject()อินสแตนซ์ได้

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

9
มันสามารถสร้างอินสแตนซ์ได้ แต่ไม่ได้ใช้เพื่อประโยชน์ใด ๆ เมื่อทำเสร็จแล้ว foo = object()ใช้งานได้ แต่คุณไม่สามารถทำอะไรได้มากกับมันเลย
Daniel DiPaolo

สวัสดี ขอบคุณสำหรับคำตอบ. ฉันได้อัปเดตปัญหาของฉันด้านบนแล้ว ดูการแก้ไข คุณรู้คำตอบนี้หรือไม่?
จอห์น

ขอโทษฉันยังต้องการที่จะตั้งไว้บนวัตถุ ดูอัปเดตด้านบน
จอห์น

ฉันชอบคำตอบของคุณและฉันคิดว่าฉันจะยันทิศทางนี้ในอนาคต ฉันได้ใช้ทุกอย่างในโพสต์นี้ยกเว้นวิธีการที่ง่ายเข้าใจและอ่านได้ การใช้type....แลมบ์ดาไม่เคยเป็นที่ชื่นชอบของฉันเช่นข้อความอาเจียนในรหัสของฉัน แต่ความคิดนี้ยอดเยี่ยมสำหรับการใช้วัตถุเพื่อเก็บคุณสมบัติ ปล่อยรหัสให้อ่านได้ง่ายขึ้นเมื่อฉันเห็นแลมบ์ดาฉันจะทำให้การอ่านของฉันช้าลงถึง 25% ในขณะที่วิธีการของคุณสมเหตุสมผล ขอบคุณ
Marc

คำตอบที่ดีสิ่งเดียวที่ฉันเปลี่ยนคือใช้Structเป็นชื่อของคลาสเพื่อให้ชัดเจนยิ่งขึ้น ช่วยฉันพิมพ์จำนวนมาก["และ"]ไชโย!
pragman

137

มีtypes.SimpleNamespaceคลาสใน Python 3.3+ :

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

collections.namedtuple, typing.NamedTupleสามารถนำมาใช้สำหรับวัตถุที่ไม่เปลี่ยนรูป PEP 557 - คลาสข้อมูล แนะนำทางเลือกที่ไม่แน่นอน

สำหรับการใช้งานที่สมบูรณ์ยิ่งขึ้นคุณสามารถลองattrsแพ็คเกจได้ ดูใช้งานที่ตัวอย่างเช่น


3
หากคุณต้องการบางสิ่งที่ทำงานร่วมกับ Python 2.7 คุณสามารถลองargparse.Namespaceคลาส
RolKau

ตกลง - ฉันอยากรู้อยากเห็นหากมีข้อเสียที่นี่ แต่นี่เป็นงูเหลือมที่มีประโยชน์อย่างเหลือเชื่อ 3.3+
ghukill

ประณาม! ไม่สามารถใช้ได้ใน 2.7?
Roel

@Roel attrsแพ็คเกจรองรับ Python 2.7
jfs

ดูเหมือนว่าทางออกที่ดีกว่าสำหรับฉันคือ unittest.mock หลังมีน้ำหนักมากเกินไปและอ่อนกว่าเล็กน้อย ด้วยวัตถุจำลองการกำหนดให้กับแอตทริบิวต์จะทำให้เกิดการมีชีวิตอยู่ SimpleNamespace จะต่อต้านสิ่งนั้น
jdzions

32

มีสองสามวิธีในการบรรลุเป้าหมายนี้ โดยทั่วไปคุณต้องมีวัตถุที่สามารถขยายได้

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

obj.b = lambda:None

class Test:
  pass
obj.c = Test()

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

26

mockโมดูลที่ทำโดยทั่วไปว่า

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

3
ข้อเสียคือการพึ่งพาภายนอก
Kangur

5
unittest.Mockเป็นส่วนหนึ่งของไลบรารีมาตรฐานตั้งแต่ Python 3.3 ( docs.python.org/3/library/unittest.mock.html )
illagrenan

2
ขึ้นอยู่กับการใช้รหัสของคุณฉันคิดว่า หากเป็นรหัสการผลิตฉันจะไม่ต้องการบางอย่างmockในนั้น แค่รู้สึกแปลก ๆ กับฉัน
Mike de Klerk

21

ตอนนี้คุณสามารถทำได้ (ไม่แน่ใจว่าเป็นคำตอบเดียวกับ evilpie):

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

คำตอบของ @ evilpie ตั้งค่าคุณสมบัติโดยตรงบน MyObject (คลาส) ไม่ใช่ตัวอย่างของคุณ
jfs

18

ลองรหัสด้านล่าง:

$ 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
คุณช่วยอธิบายเพิ่มเติมเกี่ยวกับสิ่งนี้ได้ไหม? ในขณะที่รหัสอาจมีประโยชน์สำหรับการแก้ปัญหานี้การอธิบายโค้ดสามารถทำได้มากกว่าปัญหาเดียว
DeadChex

1
@DeadChex เห็นได้ชัดว่ามันจะสร้าง Class ใหม่ (วัตถุ) ซึ่งเป็นคลาสที่ว่างเปล่าที่มีคุณสมบัติของวัตถุและเก็บคุณสมบัติในชั้นเรียน นี่จะดีกว่าการติดตั้งโมดูลเพิ่มเติมหรือพึ่งพา lambdas
m3nda

2
ไม่แน่ใจว่าทำไมถึงไม่มี upvotes มากกว่านี้ มีเหตุผลที่จะไม่ใช้สิ่งนี้สำหรับคลาสคอนเทนเนอร์พื้นฐานหรือไม่? ดูเหมือนว่าจะทำงานได้ดีใน Python 2.7, 2.6 และ 3.4
user5359531

17

คุณยังสามารถใช้คลาสอ็อบเจ็กต์โดยตรง มันสร้างเนมสเปซ:

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

9

ในขณะที่เอกสารพูดว่า :

หมายเหตุ : objectไม่ได้มี__dict__เพื่อให้คุณไม่สามารถคุณลักษณะโดยพลการกำหนดตัวอย่างของที่objectชั้น

คุณสามารถใช้อินสแตนซ์หุ่นจำลองได้


2

โซลูชั่นเหล่านี้มีประโยชน์มากในระหว่างการทดสอบ การสร้างคำตอบของคนอื่นฉันทำสิ่งนี้ใน Python 2.7.9 (ไม่มี staticmethod ฉันได้รับ TypeError (unbound method ... ):

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

1

คุณใช้วัตถุใดอยู่ แค่ลองด้วยคลาสตัวอย่างและมันใช้ได้ดี:

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

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

และฉันก็ได้123คำตอบ

สถานการณ์เดียวที่ฉันเห็นความล้มเหลวนี้คือถ้าคุณกำลังลองsetattrกับวัตถุในตัว

อัปเดต: จากความคิดเห็นนี่คือการทำซ้ำของ: ทำไมคุณไม่สามารถเพิ่มคุณสมบัติให้กับวัตถุในไพ ธ อนได้?


bc ถูกตั้งค่าเป็น object () ไม่ใช่คลาสที่กำหนด
John

0

มาที่นี่ในช่วงปลายวัน แต่นี่คือ pennyworth ของฉันกับวัตถุที่เพิ่งเกิดขึ้นเพื่อเก็บเส้นทางที่มีประโยชน์บางอย่างในแอพ แต่คุณสามารถปรับเปลี่ยนได้ทุกสิ่งที่คุณต้องการข้อมูลที่สามารถเข้าถึงได้ด้วย getattr และ dot notation (ซึ่งเป็นสิ่งที่ฉันคิดว่าคำถามนี้เป็นจริงเกี่ยวกับ):

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))

นี่มันเท่ห์เพราะตอนนี้:

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

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

ดังนั้นสิ่งนี้ใช้ฟังก์ชั่นวัตถุเช่นคำตอบข้างต้น แต่ใช้ฟังก์ชั่นเพื่อรับค่า (คุณยังสามารถใช้งานได้(getattr, x_path, 'repository')มากกว่าที่x_path('repository')คุณต้องการ)


0

หากเราสามารถกำหนดและรวมคุณสมบัติและค่าทั้งหมดเข้าด้วยกันก่อนที่จะสร้างออบเจ็กต์ซ้อนเราสามารถสร้างคลาสใหม่ที่รับอาร์กิวเมนต์ของพจนานุกรมในการสร้าง

# 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'

นอกจากนี้เรายังสามารถอนุญาตให้มีการขัดแย้งกับคำหลัก ดูโพสต์นี้

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

วิธีอื่นที่ฉันเห็นด้วยวิธีนี้:

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)

คุณสามารถเพิ่มชื่อต่อท้ายนี้ di [name] .append ([at, cmds.getAttr (ชื่อ + '.' + ที่) [0]])
Pablo Emmanuel De Leo

1
นี่คือการเพิ่มการพึ่งพาที่ไม่ได้มาตรฐานขนาดใหญ่มากในขณะที่ความเรียบง่ายclass a: passให้พลังงานทั้งหมดที่จำเป็น
Alexis Paques
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.