亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在元類中鍵入提示動態設置的類屬性?

如何在元類中鍵入提示動態設置的類屬性?

慕村9548890 2021-11-23 16:33:16
當我動態設置類的屬性時:from typing import TypeVar, Generic, Optional, ClassVar, Anyclass IntField:    type = intclass PersonBase(type):    def __new__(cls):        for attr, value in cls.__dict__.items():            if not isinstance(value, IntField):                continue            setattr(cls, attr, value.type())        return clsclass Person(PersonBase):    age = IntField()person = Person()print(type(Person.age)) # <class 'int'>print(type(person.age)) # <class 'int'>person.age = 25 # Incompatible types in assignment (expression has type "int", variable has type "IntField")該類型的age屬性將是類型int,但MyPy不能遵循。有沒有辦法讓 MyPy 理解?Django 已經實現了:from django.db import modelsclass Person(models.Model):    age = models.IntegerField()person = Person()print(type(Person.age)) # <class 'django.db.models.query_utils.DeferredAttribute'>print(type(person.age)) # <class 'int'>person.age = 25  # No errorDjango 是如何做到這一點的?
查看完整描述

2 回答

?
UYOU

TA貢獻1878條經驗 獲得超4個贊

由于您在類上定義字段,實用的方法是對字段進行類型提示。請注意,您必須告訴mypy不要檢查線路本身。


class Person(PersonBase):

    age: int = IntField()  # type: ignore

這是最小的變化,但相當不靈活。


您可以使用帶有假簽名的輔助函數來創建自動鍵入的通用提示:


from typing import Type, TypeVar



T = TypeVar('T')



class __Field__:

    """The actual field specification"""

    def __init__(self, *args, **kwargs):

        self.args, self.kwargs = args, kwargs



def Field(tp: Type[T], *args, **kwargs) -> T:

    """Helper to fake the correct return type"""

    return __Field__(tp, *args, **kwargs)  # type: ignore



class Person:

    # Field takes arbitrary arguments

    # You can @overload Fields to have them checked as well

    age = Field(int, True, object())

這就是attrs庫提供其遺留提示的方式。這種風格允許隱藏注釋的所有魔法/技巧。


由于元類可以檢查注釋,因此無需在 Field 上存儲類型。您可以Field對元數據使用裸,對類型使用注釋:


from typing import Any



class Field(Any):  # the (Any) part is only valid in a .pyi file!

    """Field description for Any type"""



class MetaPerson(type):

    """Metaclass that creates default class attributes based on fields"""

    def __new__(mcs, name, bases, namespace, **kwds):

        for name, value in namespace.copy().items():

            if isinstance(value, Field):

                # look up type from annotation

                field_type = namespace['__annotations__'][name]

                namespace[name] = field_type()

        return super().__new__(mcs, name, bases, namespace, **kwds)



class Person(metaclass=MetaPerson):

    age: int = Field()

這就是attrs提供其 Python 3.6+ 屬性的方式。它既通用又符合注釋風格。請注意,這也可以與常規基類而不是元類一起使用。


class BasePerson:

     def __init__(self):

         for name, value in type(self).__dict__.items():

             if isinstance(value, Field):

                 field_type = self.__annotations__[name]

                 setattr(self, name, field_type())



class Person(BasePerson):

    age: int = Field()


查看完整回答
反對 回復 2021-11-23
?
catspeake

TA貢獻1111條經驗 獲得超0個贊

Patrick Haugh 是對的,我試圖以錯誤的方式解決這個問題。描述符是要走的路:


from typing import TypeVar, Generic, Optional, ClassVar, Any, Type


FieldValueType = TypeVar('FieldValueType')



class Field(Generic[FieldValueType]):


    value_type: Type[FieldValueType]


    def __init__(self) -> None:

        self.value: FieldValueType = self.value_type()


    def __get__(self, obj, objtype) -> 'Field':

        print('Retrieving', self.__class__)

        return self


    def __set__(self, obj, value):

        print('Updating', self.__class__)

        self.value = value


    def to_string(self):

        return self.value


class StringField(Field[str]):

    value_type = str


class IntField(Field[int]):

    value_type = int


    def to_string(self):

        return str(self.value)



class Person:

    age = IntField()


person = Person()

person.age = 25

print(person.age.to_string())

MyPy可以完全理解這一點。謝謝!


查看完整回答
反對 回復 2021-11-23
  • 2 回答
  • 0 關注
  • 174 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號