<p>好吧,这里有三个混淆点。对象标识、描述符协议和动态属性。在</p>
<p>首先,将<code>__dbattr__</code>分配给<code>func</code>。在</p>
<pre><code>def __call__(self , func):
func.__dbattr__ = self.default # you don't need setattr
def validate(obj , value):
func(obj , value)
return validate
</code></pre>
<p>但这是将属性赋给<code>func</code>,它只作为<code>validate</code>的一个成员持有,而{<cd4>}又会替换类中的{<cd2>}(这就是装饰器最终要做的,用另一个函数替换一个函数)。因此,通过将这些数据放在<code>func</code>上,我们就失去了对它的访问权(如果没有一些严重的黑客攻击<code>__closure__</code>访问)。相反,我们应该把数据放在<code>validate</code>。在</p>
^{pr2}$
<hr/>
<p>现在,<code>u.Name.__dbattr__</code>有用吗?不,您仍然会得到相同的错误,但是数据现在可以访问了。要找到它,我们需要理解python的<a href="https://docs.python.org/3.7/howto/descriptor.html" rel="nofollow noreferrer">descriptor protocol</a>,它定义了属性如何工作。在</p>
<p>请阅读链接的文章以获得完整的解释,但实际上,<code>@property</code>的工作原理是使用<code>__get__</code>、<code>__set__</code>和{<cd13>}方法创建一个附加的类,当您调用<code>inst.property</code>时,您实际要做的是调用<code>inst.__class__.property.__get__(inst, inst.__class__)</code>(和{<cd16>}和{<cd17>}()类似)。这些方法依次调用<code>fget</code>、<code>fset</code>和{<cd20>}方法,它们是对类中定义的方法的引用。在</p>
<p>所以我们可以找到你的<code>__dbattr__</code>,而不是<code>u.Name</code>(这是<code>User.Name.__get__(u, User)</code>的结果,而是<code>User.Name.fset</code>方法本身!如果你仔细想想,这是有道理的。这就是你使用的方法。你没有把它放在结果的价值上!在</p>
<pre><code>User.Name.fset.__dbattr__
Out[223]: {'length': 100, 'required': False, 'type': 'string'}
</code></pre>
<hr/>
<p>对,所以我们可以看到这个数据存在,但它不在我们想要的对象上。我们怎么把它放到那个物体上?其实很简单。在</p>
<pre><code>def __call__(self , func):
def validate(obj , value):
# Set the attribute on the *value* we're going to pass to the setter
value.__dbattr__ = self.default
func(obj , value)
return validate
</code></pre>
<p>这只在setter最终返回值时有效,但在您的例子中确实如此。在</p>
<pre><code># Using a custom string class (will explain later)
from collections import UserString
u = User()
u.Name = UserString('hello')
u.Name # > 'hello'
u.Name.__dbattr__ # >{'length': 100, 'required': False, 'type': 'string'}
</code></pre>
<hr/>
<p>你可能想知道我为什么使用自定义字符串类。如果你用一个基本的字符串,你会发现问题的</p>
<pre><code>u.Name = 'hello'
-
AttributeError Traceback (most recent call last)
<ipython-input-238-1feeee60651f> in <module>()
> 1 u.Name = 'hello'
<ipython-input-232-8529bc6984c8> in validate(obj, value)
6
7 def validate(obj , value):
> 8 value.__dbattr__ = self.default
9 func(obj , value)
10 return validate
AttributeError: 'str' object has no attribute '__dbattr__'
</code></pre>
<p><code>str</code>对象和python中的大多数内置类型一样,不允许像自定义python类那样随机分配属性(<code>collections.UserString</code>是一个python类包装器,它允许随机赋值)。在</p>
<p>因此,最终,您最初想要的东西是不可能的内置字符串,但使用自定义类允许您这样做。在</p>