簡體   English   中英

類變量作為封閉類的實例

[英]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作為屬性,因為這是一個保留屬性(用於反映枚舉值名稱,這里是BLACKWHITE ):

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM