1. 程式人生 > >Python中子類如何獲取父類的類成員

Python中子類如何獲取父類的類成員

@bwangel

大家好,今天在寫程式碼的時候,遇到了這樣一種情況。我有如下所示的幾個類用來存放程式配置(其實當做名稱空間來用,同時感覺能夠繼承方便一點),

  1. import os
  2. classConfig:
  3. BASE_DIR ="/tmp"
  4. classTestConfig(Config):
  5. DATA_DIR = os.path.join(Config.BASE_DIR,"data")

然後我在子類中想要訪問父類的類成員變數,而且這兩個類都是隻有類成員變數。感覺目前我使用的方法笨一點,就是直接引用父類的名字,感覺這樣的方法不靈活,我想找一種方法,可以讓子類訪問到父類。

我在網上搜索了一下,找了這麼兩種方法,但是感覺都不怎麼符合我的需求:

  1. 1. 在子類方法中呼叫super(TestConfig, self)來獲取父類(我的類只有類成員變數,沒有self)
  2. 2. 通過子類的名字SubConfig.__bases__來獲取父類(我是在SubConfing這個子類內部執行相關語句的,會丟擲SubConfig還未定義的NameError

然後就沒有找到其他的辦法了,所以想來和大家請教一下,像我這種想法,有辦法可以實現嗎?應該怎麼做啊?這個問題問的可能比較傻,還請大家不要見怪。

@Python Yiyi 

利用Python3 metaclass 實現

>>> import os
>>> class M(type):
	@classmethod
	def __prepare__(metacls, name, bases, **kwds):
		d = dict()
		for base in bases:
			for key, value in base.__dict__.items():
				if not key.startswith('_'):
					d[key] = value
		return d
	def __new__(cls, name, bases, namespace, **kwds):
		for base in bases:
			for key, value in base.__dict__.items():
				if not key.startswith('_'):
				        del namespace[key]
		return type.__new__(cls, name, bases, dict(namespace))

	
>>> class Config(metaclass=M):
	BASE_DIR = "/tmp"

	
>>> class TestConfig(Config):
	DATA_DIR = os.path.join(BASE_DIR, "data")

	
>>> TestConfig.DATA_DIR
'/tmp\\data'
>>>  
>>> TestConfig.__dict__
mappingproxy({'__doc__': None, '__module__': '__main__', 'DATA_DIR': '/tmp\\data'})
>>> 


附上上述程式碼的解釋,基本都來自於Python 語言參考中描述:

當執行類定義時,將執行以下步驟:

  • 確定正確的元類
  • 準備類的名稱空間
  • 執行類的主體
  • 建立類物件

3.3.3.1. 確定正確的元類

3.3.3.2. 準備類的名稱空間

確定正確的元類後,則開始準備類的名稱空間。如果元類具有__prepare__屬性,那麼它以namespace = metaclass.__prepare__(name, bases, **kwds)形式呼叫(其中如果有額外的關鍵字引數,那麼它們來自類的定義)。

如果元類沒有__prepare__屬性,那麼類的名稱空間初始化一個空的dict()例項。

3.3.3.3. 
執行類的主體

類的主體(大體上)以exec(body, globals(), namespace)的方式執行。(從這裡可以看出,BASE_DIR找不到的原因是globals() 和namespace 中沒有BASE_DIR定義。解決辦法是將基類的成員拷貝到namespace中)

3.3.3.4. 建立類物件

類的名稱空間通過執行類的主體建立完之後,通過呼叫metaclass(name, bases, namespace, **kwds)建立類物件(這裡傳遞過來的額外的關鍵字引數與傳遞給__prepare__的相同)。