[英]Detect re-declaration of Python class variables
Let's say I have a Python class as below:假设我有一个 Python 类,如下所示:
class Foo:
one = 'some value'
two = 'some other value'
# ... and so on ...
twenty = 'blah blah'
one = 'updated value' # OH NOEEESSS :o
I want to detect (and possibly prevent) the re-declaration of Foo.one
in the class body.我想检测(并可能阻止)在类主体中重新声明
Foo.one
。
Is there any way to detect this programmatically at runtime, or even via a lint rule?有没有办法在运行时以编程方式甚至通过 lint 规则检测到这一点?
If it helps, my use case is a bit more specific.如果有帮助,我的用例会更具体一些。 I want to avoid re-using column names of Flask SQLAlchemy models, where I have a long list of columns and I risk re-using old column names while trying to add new columns.
我想避免重复使用Flask SQLAlchemy模型的列名,我有很长的列列表,并且在尝试添加新列时我冒着重新使用旧列名的风险。 For example:
例如:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class UserModel(db.Model):
""" A sample user model """
__tablename__ = 'user'
name = db.Column(db.String)
is_dead = db.Column(db.Boolean)
# ... and so on ...
name = db.Column(db.String) # Detect/prevent re-declaration of 'name'
I managed to solve for my specific usecase at runtime using a metaclass, a special dictionary and by overriding the __prepare__
method of the metaclass:我设法在运行时使用元类、特殊字典并通过覆盖元类的
__prepare__
方法解决了我的特定用例:
from sqlalchemy.sql.schema import Column
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class NoColumnReassignmentDict(dict):
def __setitem__(self, key, value):
if key in self and isinstance(self[key], Column):
raise Exception(f'Cannot re-assign column <{key}>')
return super().__setitem__(key, value)
class MetaBaseModel(type(db.Model)):
@classmethod
def __prepare__(cls, *args, **kwargs):
return NoColumnReassignmentDict()
class UserModel(db.Model, metaclass=MetaBaseModel):
""" A sample user model """
__tablename__ = 'user'
user_id = db.Column(db.String, nullable=False, primary_key=True)
name = db.Column(db.String)
is_dead = db.Column(db.Boolean)
# ... and so on ...
name = db.Column(db.String) # Raises: 'Exception: Cannot re-assign column <name>'
I suppose this could be extended for a generic usecase too.我想这也可以扩展到通用用例。
I'm not entirely sure this will work;我不完全确定这会起作用; I don't recall if
db.Model
uses a metaclass that could interfere with this.我不记得
db.Model
使用了可能会干扰这个的元类。 But instead of using a class
statement, you can call type
directly with a dict of attributes which has been prescreened for duplicates.但是,您可以使用已预先筛选重复项的属性字典直接调用
type
,而不是使用class
语句。
def checkForDuplicateFields(fields):
seen = set()
for k in fields:
if k in seen:
raise ValueError(f"Duplicate key {k} found")
seen.add(k)
fields = [
('name', db.Column(db.String)),
...
]
checkForDuplicateFields(fields)
UserModel = type('UserModel', (db.Model,), dict(fields))
(If db.Model
does use some other metaclass, for example DBMeta
, then you would call that instead of type
: (如果
db.Model
确实使用了其他一些元类,例如DBMeta
,那么您将调用它而不是type
:
UserModel = DBMeta('UserModel', (db.Model,) dict(fields))
) )
(I'm not sure this is something even a custom metaclass could handle. The way a class
statement works, the names assigned in the body are gathered into a dict
and passed as an argument to the metaclass. You would need the class
statement to use a special kind of dict
that rejects duplicate assignments (or at least a multi dict, which doesn't exist in standard Python) before the metaclass is even invoked, and I don't know if there is any hook to allow that.) (我不确定这是什至自定义元类可以处理的事情。
class
语句的工作方式,主体中分配的名称被收集到一个dict
并作为参数传递给元类。您需要class
语句来在元类被调用之前,使用一种特殊的dict
拒绝重复赋值(或者至少是一个 multi dict,它在标准 Python 中不存在),我不知道是否有任何钩子允许这样做。)
How about checking if the attribute is set first?如何检查属性是否首先设置?
class Foo:
def __init__(self):
self.one = 'some value'
self.two = 'some other value'
# ... and so on ...
self.twenty = 'blah blah'
if not hasattr(self, 'one'):
self.one = 'updated value' # Aww yea
In case the variables are in the form of config or if their string names are known:如果变量是 config 的形式或者它们的字符串名称是已知的:
class Foo:
def __init__(self):
self.one = 'some value'
self.two = 'some other value'
# ... and so on ...
self.twenty = 'blah blah'
if not hasattr(self, 'one'):
setattr(self, 'one', 'updated value') # Aww yea 2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.