Hãy nhìn vào đoạn mã sau:Làm cách nào để truy cập các thuộc tính lớp của lớp cha trong Python?
class A(object):
defaults = {'a': 1}
def __getattr__(self, name):
print('A.__getattr__')
return self.get_default(name)
@classmethod
def get_default(cls, name):
# some debug output
print('A.get_default({}) - {}'.format(name, cls))
try:
print(super(cls, cls).defaults) # as expected
except AttributeError: #except for the base object class, of course
pass
# the actual function body
try:
return cls.defaults[name]
except KeyError:
return super(cls, cls).get_default(name) # infinite recursion
#return cls.__mro__[1].get_default(name) # this works, though
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c = C()
print('c.a =', c.a)
tôi có một hệ thống các lớp học mỗi từ điển riêng của mình có chứa một số giá trị mặc định. Nếu một thể hiện của một lớp không có một thuộc tính cụ thể, thay vào đó một giá trị mặc định cho lớp đó sẽ được trả về. Nếu không có giá trị mặc định cho thuộc tính được chứa trong từ điển hiện tại của lớp defaults
, từ điển của lớp học defaults
sẽ được tìm kiếm.
Tôi đang cố thực hiện điều này bằng phương pháp lớp đệ quy get_default
. Chương trình bị mắc kẹt trong một đệ quy vô hạn, thật không may. Sự hiểu biết của tôi về super()
rõ ràng là thiếu. Bằng cách truy cập __mro__
, tôi có thể làm cho nó hoạt động đúng mặc dù, nhưng tôi không chắc chắn đây là một giải pháp thích hợp.
Tôi có cảm giác câu trả lời ở đâu đó trong số this article, nhưng tôi chưa thể tìm thấy câu trả lời. Có lẽ tôi cần phải sử dụng một metaclass?
chỉnh sửa: Trong đơn đăng ký của tôi, __getattr__
kiểm tra đầu tiên self.base
. Nếu nó không phải là None
, thuộc tính cần được tìm nạp từ đó. Chỉ trong trường hợp khác, giá trị mặc định phải được trả về. Tôi có thể có thể ghi đè lên __getattribute__
. Đó có phải là giải pháp tốt hơn không?
chỉnh sửa 2: Dưới đây là ví dụ mở rộng về chức năng mà tôi đang tìm kiếm. Nó hiện đang được thực hiện bằng cách sử dụng __mro__
(đề xuất trước đó của unutbu, trái với phương pháp đệ quy ban đầu của tôi). Trừ khi ai đó có thể đề xuất một giải pháp thanh lịch hơn, tôi rất vui khi sử dụng triển khai này. Tôi hy vọng điều này sẽ làm mọi thứ rõ ràng.
class A(object):
defaults = {'a': 1}
def __init__(self, name, base=None):
self.name = name
self.base = base
def __repr__(self):
return self.name
def __getattr__(self, name):
print(" '{}' attribute not present in '{}'".format(name, self))
if self.base is not None:
print(" getting '{}' from base ({})".format(name, self.base))
return getattr(self.base, name)
else:
print(" base = None; returning default value")
return self.get_default(name)
def get_default(self, name):
for cls in self.__class__.__mro__:
try:
return cls.defaults[name]
except KeyError:
pass
raise KeyError
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c1 = C('c1')
c1.b = 55
print('c1.a = ...'); print(' ...', c1.a) # 1
print(); print('c1.b = ...'); print(' ...', c1.b) # 55
print(); print('c1.c = ...'); print(' ...', c1.c) # 3
c2 = C('c2', base=c1)
c2.c = 99
print(); print('c2.a = ...'); print(' ...', c2.a) # 1
print(); print('c2.b = ...'); print(' ...', c2.b) # 55
print(); print('c2.c = ...'); print(' ...', c2.c) # 99
Sản lượng:
c1.a = ...
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c1.b = ...
... 55
c1.c = ...
'c' attribute not present in 'c1'
base = None; returning default value
... 3
c2.a = ...
'a' attribute not present in 'c2'
getting 'a' from base (c1)
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c2.b = ...
'b' attribute not present in 'c2'
getting 'b' from base (c1)
... 55
c2.c = ...
... 99
Làm cách nào để đặt mặc định trực tiếp vào không gian tên lớp thay vì sử dụng từ điển 'mặc định'? Điều này sẽ làm cho bất kỳ ma thuật không cần thiết - nó sẽ chỉ hoạt động. –
@Sven Tôi biết tôi nên đã đề cập đến điều này :) Trong ứng dụng của tôi, '__getattr__' là lần đầu tiên kiểm tra một thuộc tính (cơ sở). Chỉ khi thuộc tính kia là None, giá trị mặc định sẽ được trả về. Nếu không, thuộc tính cần phải được tìm kiếm trong cơ sở. –
Vâng, đó là cách các thuộc tính instance/class hoạt động trong Python. :) Kiểm tra câu trả lời của Toni. Afaik mã của mình sẽ làm chính xác giống như của bạn. –