簡體   English   中英

檢測 Python 類變量的重新聲明

[英]Detect re-declaration of Python class variables

假設我有一個 Python 類,如下所示:

class Foo:
    one = 'some value'
    two = 'some other value'
    # ... and so on ...
    twenty = 'blah blah'
    one = 'updated value'  # OH NOEEESSS :o

我想檢測(並可能阻止)在類主體中重新聲明Foo.one

有沒有辦法在運行時以編程方式甚至通過 lint 規則檢測到這一點?


如果有幫助,我的用例會更具體一些。 我想避免重復使用Flask SQLAlchemy模型的列名,我有很長的列列表,並且在嘗試添加新列時我冒着重新使用舊列名的風險。 例如:

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'

我設法在運行時使用元類、特殊字典並通過覆蓋元類的__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>'

我想這也可以擴展到通用用例。


PS: 是一個很好的參考!

我不完全確定這會起作用; 我不記得db.Model使用了可能會干擾這個的元類。 但是,您可以使用已預先篩選重復項的屬性字典直接調用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))

(如果db.Model確實使用了其他一些元類,例如DBMeta ,那么您將調用它而不是type

UserModel = DBMeta('UserModel', (db.Model,) dict(fields))

)

(我不確定這是什至自定義元類可以處理的事情。 class語句的工作方式,主體中分配的名稱被收集到一個dict並作為參數傳遞給元類。您需要class語句來在元類被調用之前,使用一種特殊的dict拒絕重復賦值(或者至少是一個 multi dict,它在標准 Python 中不存在),我不知道是否有任何鈎子允許這樣做。)

如何檢查屬性是否首先設置?

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

如果變量是 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM