创建第一个-class 对象,它的所有实例属性都是只读的,就像切片一样?
Create first-class object all of it's instance attributes are readonly like slice?
我的问题是如何创建像 slice
这样的 class?
slice
(内置类型)没有 __dict__
属性
即使 slice
的 metaclass
是 type
.
它没有使用 __slots__
,它的所有属性都是只读的,它没有被覆盖
__setattr__
(对此我不确定,但请查看我的代码,看看我是否正确)。
检查此代码:
# how slice is removing the __dict__ from the class object
# and the metaclass is type!!
class sliceS(object):
pass
class sliceS0(object):
def __setattr__(self, name, value):
pass
# this means that both have the same
# metaclass type.
print type(slice) == type(sliceS) # prints True
# from what i understand the metaclass is the one
# that is responsible for making the class object
sliceS2 = type('sliceS2', (object,), {})
# witch is the same
# sliceS2 = type.__new__(type, 'sliceS2', (object,), {})
print type(sliceS2) # prints type
# but when i check the list of attribute using dir
print '__dict__' in dir(slice) # prints False
print '__dict__' in dir(sliceS) # prints True
# now when i try to set an attribute on slice
obj_slice = slice(10)
# there is no __dict__ here
print '__dict__' in dir(obj_slice) # prints False
obj_sliceS = sliceS()
try:
obj_slice.x = 1
except AttributeError as e:
# you get AttributeError
# mean you cannot add new properties
print "'slice' object has no attribute 'x'"
obj_sliceS.x = 1 # Ok: x is added to __dict__ of obj_sliceS
print 'x' in obj_sliceS.__dict__ # prints True
# and slice is not using __slots__ because as you see it's not here
print '__slots__' in dir(slice) # print False
# and this why i'm saying it's not overriding the __settattr__
print id(obj_slice.__setattr__) == id(obj_sliceS.__setattr__) # True: it's the same object
obj_sliceS0 = sliceS0()
print id(obj_slice.__setattr__) == id(obj_sliceS0.__setattr__) # False: it's the same object
# so slice have only start, stop, step and are all readonly attribute and it's not overriding the __setattr__
# what technique it's using?!!!!
如何制作这种第一个class对象,它的所有属性都是只读的,你不能
添加新属性。
问题是 Python 的内置 slice
class 是用 C 编写的。当您使用 C-Python API 您可以编写与 __slots__
可访问的属性等效的代码,而无需使用从 Python 端可见的任何机制。 (您甚至可以拥有 'real' 私有属性,这对于仅使用 Python 代码几乎是不可能的。
用于 Python 代码的机制能够防止 class' 实例和后续 "any attribute can be set" 的 __dict__
正是 __slots__
属性。
然而,与实际使用 class 时必须存在的魔术 dunder 方法不同,__slots__
上的信息在创建 class 时使用,并且仅在创建时使用。因此,如果您担心在最终 class 中有一个可见的 __slots__
,您可以在公开它之前将其从 class 中删除:
In [8]: class A:
...: __slots__ = "b"
...:
In [9]: del A.__slots__
In [10]: a = A()
In [11]: a.b = 5
In [12]: a.c = 5
------------------------
AttributeError
...
In [13]: A.__slots__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-13-68a69c802e74> in <module>()
----> 1 A.__slots__
AttributeError: type object 'A' has no attribute '__slots__'
如果您不希望 del MyClass.__slots__
行在您声明 class 的任何地方都可见,那么它是单行 class 装饰器:
def slotless(cls):
del cls.__slots__
return cls
@slotless
class MyClass:
__slots__ = "x y".split()
或者,您可以使用元class 自动创建和自动销毁 Python 可见 __slots__
,这样您就可以在class 正文,并保护 class 不受额外属性的影响:
class AttrOnly(type):
def __new__(metacls, name, bases, namespace, **kw):
namespace["__slots__"] = list(namespace.keys()) # not sure if "list(" is needed
cls = super().__new__(metacls, name, bases, namespace, **kw)
del cls.__slots__
return cls
class MyClass(metaclass=AttrOnly):
x = int
y = int
如果你想要纯 Python 只读属性,它在实例本身中没有可见的对应物(比如 ._x
被 property
描述符用来保持值x
属性),直接的方法是自定义 __setattr__
。另一种方法是让您的 metaclass 在 class 创建阶段为每个属性自动添加只读 属性。下面的 metaclass 执行此操作并使用 __slots__
class 属性创建所需的描述符:
class ReadOnlyAttrs(type):
def __new__(metacls, name, bases, namespace, **kw):
def get_setter(attr):
def setter(self, value):
if getattr(self, "_initialized", False):
raise ValueError("Can't set " + attr)
setattr(self, "_" + attr, value)
return setter
slots = namespace.get("__slots__", [])
slots.append("initialized")
def __new__(cls, *args, **kw):
self = object.__new__(cls) # for production code that could have an arbitrary hierarchy, this needs to be done more carefully
for attr, value in kw.items():
setattr(self, attr, value)
self.initialized = True
return self
namespace["__new__"] = __new__
real_slots = []
for attr in slots:
real_slots.append("_" + attr)
namespace[attr] = property(
(lambda attr: lambda self: getattr(self, "_" + attr))(attr), # Getter. Extra lambda needed to create an extra closure containing each attr
get_setter(attr)
)
namespace["__slots__"] = real_slots
cls = super().__new__(metacls, name, bases, namespace, **kw)
del cls.__slots__
return cls
请记住,您还可以自定义 class' __dir__
方法,以便 _x
阴影属性不会被看到,如果您愿意的话。
我的问题是如何创建像 slice
这样的 class?
slice
(内置类型)没有 __dict__
属性
即使 slice
的 metaclass
是 type
.
它没有使用 __slots__
,它的所有属性都是只读的,它没有被覆盖
__setattr__
(对此我不确定,但请查看我的代码,看看我是否正确)。
检查此代码:
# how slice is removing the __dict__ from the class object
# and the metaclass is type!!
class sliceS(object):
pass
class sliceS0(object):
def __setattr__(self, name, value):
pass
# this means that both have the same
# metaclass type.
print type(slice) == type(sliceS) # prints True
# from what i understand the metaclass is the one
# that is responsible for making the class object
sliceS2 = type('sliceS2', (object,), {})
# witch is the same
# sliceS2 = type.__new__(type, 'sliceS2', (object,), {})
print type(sliceS2) # prints type
# but when i check the list of attribute using dir
print '__dict__' in dir(slice) # prints False
print '__dict__' in dir(sliceS) # prints True
# now when i try to set an attribute on slice
obj_slice = slice(10)
# there is no __dict__ here
print '__dict__' in dir(obj_slice) # prints False
obj_sliceS = sliceS()
try:
obj_slice.x = 1
except AttributeError as e:
# you get AttributeError
# mean you cannot add new properties
print "'slice' object has no attribute 'x'"
obj_sliceS.x = 1 # Ok: x is added to __dict__ of obj_sliceS
print 'x' in obj_sliceS.__dict__ # prints True
# and slice is not using __slots__ because as you see it's not here
print '__slots__' in dir(slice) # print False
# and this why i'm saying it's not overriding the __settattr__
print id(obj_slice.__setattr__) == id(obj_sliceS.__setattr__) # True: it's the same object
obj_sliceS0 = sliceS0()
print id(obj_slice.__setattr__) == id(obj_sliceS0.__setattr__) # False: it's the same object
# so slice have only start, stop, step and are all readonly attribute and it's not overriding the __setattr__
# what technique it's using?!!!!
如何制作这种第一个class对象,它的所有属性都是只读的,你不能 添加新属性。
问题是 Python 的内置 slice
class 是用 C 编写的。当您使用 C-Python API 您可以编写与 __slots__
可访问的属性等效的代码,而无需使用从 Python 端可见的任何机制。 (您甚至可以拥有 'real' 私有属性,这对于仅使用 Python 代码几乎是不可能的。
用于 Python 代码的机制能够防止 class' 实例和后续 "any attribute can be set" 的 __dict__
正是 __slots__
属性。
然而,与实际使用 class 时必须存在的魔术 dunder 方法不同,__slots__
上的信息在创建 class 时使用,并且仅在创建时使用。因此,如果您担心在最终 class 中有一个可见的 __slots__
,您可以在公开它之前将其从 class 中删除:
In [8]: class A:
...: __slots__ = "b"
...:
In [9]: del A.__slots__
In [10]: a = A()
In [11]: a.b = 5
In [12]: a.c = 5
------------------------
AttributeError
...
In [13]: A.__slots__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-13-68a69c802e74> in <module>()
----> 1 A.__slots__
AttributeError: type object 'A' has no attribute '__slots__'
如果您不希望 del MyClass.__slots__
行在您声明 class 的任何地方都可见,那么它是单行 class 装饰器:
def slotless(cls):
del cls.__slots__
return cls
@slotless
class MyClass:
__slots__ = "x y".split()
或者,您可以使用元class 自动创建和自动销毁 Python 可见 __slots__
,这样您就可以在class 正文,并保护 class 不受额外属性的影响:
class AttrOnly(type):
def __new__(metacls, name, bases, namespace, **kw):
namespace["__slots__"] = list(namespace.keys()) # not sure if "list(" is needed
cls = super().__new__(metacls, name, bases, namespace, **kw)
del cls.__slots__
return cls
class MyClass(metaclass=AttrOnly):
x = int
y = int
如果你想要纯 Python 只读属性,它在实例本身中没有可见的对应物(比如 ._x
被 property
描述符用来保持值x
属性),直接的方法是自定义 __setattr__
。另一种方法是让您的 metaclass 在 class 创建阶段为每个属性自动添加只读 属性。下面的 metaclass 执行此操作并使用 __slots__
class 属性创建所需的描述符:
class ReadOnlyAttrs(type):
def __new__(metacls, name, bases, namespace, **kw):
def get_setter(attr):
def setter(self, value):
if getattr(self, "_initialized", False):
raise ValueError("Can't set " + attr)
setattr(self, "_" + attr, value)
return setter
slots = namespace.get("__slots__", [])
slots.append("initialized")
def __new__(cls, *args, **kw):
self = object.__new__(cls) # for production code that could have an arbitrary hierarchy, this needs to be done more carefully
for attr, value in kw.items():
setattr(self, attr, value)
self.initialized = True
return self
namespace["__new__"] = __new__
real_slots = []
for attr in slots:
real_slots.append("_" + attr)
namespace[attr] = property(
(lambda attr: lambda self: getattr(self, "_" + attr))(attr), # Getter. Extra lambda needed to create an extra closure containing each attr
get_setter(attr)
)
namespace["__slots__"] = real_slots
cls = super().__new__(metacls, name, bases, namespace, **kw)
del cls.__slots__
return cls
请记住,您还可以自定义 class' __dir__
方法,以便 _x
阴影属性不会被看到,如果您愿意的话。