[英]Class Variable as Instance of Enclosing Class
我知道Python可以做枚舉(很好),但它所做的枚舉類型非常原始。 例如,我可以這樣做:
from enum import Enum
class Color(Enum):
BLACK = 'black'
WHITE = 'white'
print(Color.BLACK.value)
這很好但是如果我希望每種顏色都有名稱和十六進制值呢? 我有幾個選項(比如使枚舉值成為字典)但我更喜歡Java的枚舉方式。 在Java中,允許枚舉包含類和類的字段和方法。 所以通常當一種語言只支持上面例子中的簡單枚舉時,我會重構代碼,類似於以下內容:
class Color(object):
BLACK = Color('black', '#000')
WHITE = Color('white', '#fff')
def __init__(self, name, hex):
self.name = name
self.hex = hex
print(Color.BLACK.name + ' ' + Color.BLACK.hex)
現在我可以有多個值,自定義方法,並且可以按名稱引用不同的字段,因為每種顏色都是一個對象。 我已經用幾種語言做了這個沒有問題,但Python似乎抱怨“名稱'顏色'沒有定義”。 我可以不在該類中創建類的實例嗎? 我的hacky解決方案是這樣做:
class Color(object):
def __init__(self, name, hex):
self.name = name
self.hex = hex
Color.BLACK = Color('black', '#000')
Color.WHITE = Color('white', '#fff')
print(Color.BLACK.name + ' ' + Color.BLACK.hex)
哪個工作得很好。 不過,我的問題是為什么課堂內不允許這些字段? 我可以添加一些東西或重組它以使其允許嗎? 提前感謝您的任何答案!
枚舉直接支持此用例。 該庫的文檔在示例部分的Planet示例中介紹了這一點:
如果
__new__()
或__init__()
,則枚舉成員的值將傳遞給這些方法:>>> class Planet(Enum): ... MERCURY = (3.303e+23, 2.4397e6) ... # ... ... ... def __init__(self, mass, radius): ... self.mass = mass # in kilograms ... self.radius = radius # in meters ... @property ... def surface_gravity(self): ... # universal gravitational constant (m3 kg-1 s-2) ... G = 6.67300E-11 ... return G * self.mass / (self.radius * self.radius)
[...]
>>> Planet.EARTH.value (5.976e+24, 6378140.0) >>> Planet.EARTH.surface_gravity 9.802652743337129
因此,對於您的具體示例,只需定義__init__
方法:
from enum import Enum
class Color(Enum):
BLACK = ('black', '#000')
WHITE = ('white', '#fff')
def __init__(self, color_name, hex):
self.color_name = color_name
self.hex = hex
print(Color.BLACK.color_name + ' ' + Color.BLACK.hex)
我沒有使用name
作為屬性,因為這是一個保留屬性(用於反映枚舉值名稱,這里是BLACK
和WHITE
):
>>> Color.BLACK
<Color.BLACK: ('black', '#000')>
>>> Color.BLACK.name
'BLACK'
>>> Color.BLACK.color_name
'black'
>>> Color.BLACK.hex
'#000'
您仍然可以使用@property
覆蓋name
屬性,但我不會偏離此處的標准。
我使用這種技術在我的Advent of Code第22天解決方案中定義病毒狀態,定義下一個狀態名稱和每個條目的方向更改。
使用元組值和@property
訪問器為元組元素命名:
from enum import Enum
class Color(Enum):
BLACK = ('black', '#000')
WHITE = ('white', '#fff')
@property
def name(self):
return self.value[0]
@property
def hex(self):
return self.value[1]
print(Color.BLACK.name)
print(Color.BLACK.hex)
輸出 :
black
#000
至於為什么你的代碼不起作用,Python類定義是必不可少的。 在您嘗試構造Color
實例時, Color
類尚不存在。
你在這里遇到“ 雞和蛋 ”問題。 因為如果構造一個類,Python必須將屬性和函數的名稱與屬性和函數的值相關聯。 如果訪問Color.abc
,則會查看是否找到相應的名稱,並返回值/函數定義。
但現在有一個問題。 如果你寫:
class Foo(object):
bar = Foo()
為什么? 那么為了構造class
,它首先必須構造屬性。 所以它必須構造一個映射到Foo()
結果的'bar'
條目,但是我們正在構建Foo
,那么如果Foo
依賴於該構造,我們如何構造Foo
。 我們不可以。 在Java中,它更簡單,因為類在編譯時是在概念上構造的。
不過,我們有一些選擇。
Color
類 我們可以先構造Color
類,然后“ 猴子補丁 ”那個類:
class Color(object):
def __init__(self, name, hex):
self.name = name
self.hex = hex
Color.black = Color('black', '#000') Color.white = Color('white', '#fff')
這里我們首先定義Color
類,然后將屬性添加到Color
類。 之后我們可以這樣做,因為現在定義了對象。
Enum
對象 我們還可以將值附加到Enum
對象:
from enum import Enum
class Color(Enum):
white = {'name': 'white', 'hex': '#fff'} black = {'name': 'black', 'hex': '#000'} @property def name(self): return self.value['name'] @property def hex(self): return self.value['hex']
我們可以為每個Enum
成員附加一個值。 例如,我們將{'name': 'white', 'hex': '#fff'}
附加到white
。 我們以后可以通過self.value
訪問該值。 所以現在我們可以通過定義屬性函數def name(self):
來定義Color.white
的屬性def name(self):
它訪問字典的'name'
鍵。
一個命名的元組解決方案怎么樣?
from collections import namedtuple
color = namedtuple('Color', ['name', 'value']) # Add attributes as you please
class Color:
BLACK = color('black', '#000')
WHITE = color('white', '#fff')
print(Color.BLACK.name, Color.BLACK.value)
輸出
黑#000
添加新的就像這樣容易
Color.RED = color('red', '#ff0')
print(Color.RED.name, Color.RED.value)
紅色#ff0
你可以通過使用元類來幫助構造Color
類來做你想做的事:
class ColorMeta(type):
def __new__(cls, class_name, parents, attrs):
NUL = object() # Sentinel.
meta_args = attrs.get('meta_args', NUL)
if meta_args is NUL:
meta_args = []
else:
del attrs['meta_args'] # Clean up so isn't part of class created.
clsobj = super().__new__(cls, class_name, parents, attrs)
for meta_arg in meta_args:
name, hex = meta_arg
color = clsobj(name, hex)
setattr(clsobj, name, color)
return clsobj
class Color(metaclass=ColorMeta):
meta_args = [('WHITE', '#fff'),
('BLACK', '#000'),]
def __init__(self, name, hex):
self.name = name
self.hex = hex
print(Color.WHITE.name + ' ' + Color.WHITE.hex)
print(Color.BLACK.name + ' ' + Color.BLACK.hex)
我認為user2357112
已經有了你正在尋找的答案,但是也可能值得研究一下namedtuples
來訪問屬性。
命名元組:
from collections import namedtuple
Color = namedtuple('Color', 'name hex')
Black = Color(name='Black', hex='#000')
print(Black.hex)
print(Black.name)
>#000
>Black
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.