Python3 : vars(self)的响应缺少属性

 

先讲一下应用场景:将实例对象全属性转换为json数据格式(定义了以下3个方法: to_json  、 to_json2、 to_dict)

分别应用了  vars(self) .keys() 和  self.__dict__.keys()

【问题描述】: vars(self)的响应时,有时会丢失实例的属性,没有返回实例属性对应的 字典key值;   self.__dict__.keys()也是一样;

【问题解决】:不知道原因,还没解决,收集相关资料如下


代码示例:

class BaseModel():

    @property
    def to_json(self):
        # import datetime
        # 获取对象所有的key
        keys = vars(self).keys()

        obj = {}
        for key in keys:
            if not key.startswith("_"):
                key = str(key)
                # 日期对象无法进行序列化,将日期对象转为字符串
                if isinstance(getattr(self, key), datetime):
                    obj[key] = getattr(self, key).strftime("%Y-%m-%d")
                elif isinstance(getattr(self, key), time):
                    obj[key] = getattr(self, key).strftime("%H:%M:%S")
                elif isinstance(getattr(self, key), date):
                    obj[key] = getattr(self, key).strftime("%Y-%m-%d")
                else:
                    obj[key] = getattr(self, key)
        return obj

    @property
    def to_json_2(self):
        # import datetime
        # 获取对象所有的key
        keys = vars(self).keys()

        obj = {}
        for key in keys:
            if not key.startswith("_"):
                key = str(key)
                # 日期对象无法进行序列化,将日期对象转为字符串
                if isinstance(getattr(self, key), datetime):
                    obj[key] = getattr(self, key).strftime("%Y-%m-%d %H:%M:%S")
                elif isinstance(getattr(self, key), time):
                    obj[key] = getattr(self, key).strftime("%H:%M:%S")
                elif isinstance(getattr(self, key), date):
                    obj[key] = getattr(self, key).strftime("%Y-%m-%d")
                else:
                    obj[key] = getattr(self, key)
        return obj

    # ------------------------------------------------------------------------------------------
    @property
    def to_dict(self):
        # import datetime
        # 获取对象所有的key
        keys = self.__dict__.keys()

        obj = {}
        for key in keys:
            if not key.startswith("_"):
                key = str(key)
                # 日期对象无法进行序列化,将日期对象转为字符串
                if isinstance(getattr(self, key), datetime):
                    obj[key] = getattr(self, key).strftime("%Y-%m-%d %H:%M:%S")
                elif isinstance(getattr(self, key), time):
                    obj[key] = getattr(self, key).strftime("%H:%M:%S")
                elif isinstance(getattr(self, key), date):
                    obj[key] = getattr(self, key).strftime("%Y-%m-%d")
                else:
                    obj[key] = getattr(self, key)
        return obj
    # ------------------------------------------------------------------------------------------

class Project(BaseModel, Base):
    """
    项目表
    """
    __tablename__ = "ds_project"
    project_id = Column(Integer, primary_key=True, autoincrement=True, unique=True, comment="项目ID")

    # 外键
    company_id = Column(Integer, nullable=True, comment="公司ID")
    department_id = Column(Integer, nullable=True, comment="部门ID")
    director_id = Column(Integer, nullable=True, comment="分管领导ID")
    pmo_id = Column(Integer, nullable=True, comment="PMO_ID")
    pm_id = Column(Integer, nullable=True, comment="项目经理ID")
    quality_assurance_id = Column(Integer, nullable=True, comment="QA_ID")
    project_name = Column(String(255), nullable=False, comment="项目名称")  # 唯一
    project_code = Column(String(120), nullable=True, unique=True, comment="项目编号")  # 唯一 自动生成(PM_产品英文名称首字母_年份_流水号)

    @classmethod
    @reconnect()
    def by_project_id(cls, is_all, project_id):
        if is_all:
            return dbSession.query(cls).filter_by(project_id=project_id).first()
        else:
            return dbSession.query(cls).filter_by(is_delete=False).filter_by(project_id=project_id).first()

<.... 略 ....>

查询某一个 Project 实例对象,并转为json

project_obj = Project.by_project_id(is_all=1, project_id=101)

project_obj.project_id   project_obj.project_name    等都有值

但是   project_obj.to_json  没有值

 


 

Python对象将它们的实例变量存储在属于对象的字典中。 vars(x)返回这个字典(和x .__ dict__一样)。另一方面,dir(x)返回x的“属性,它的类的属性,以及递归的类的基类的属性”的字典。

当你使用点运算符访问对象的属性时,python不仅仅是查找对象字典中的属性。一个常见的情况是当x是C类的对象,你调用方法m。

 

class C(object):
    def m(self):
        print "m"

x = C()
x.m()

方法m不存储在x .__ dict__中。它是类C的属性。
当你调用x.m(),python将开始寻找m在x .__ dict__,但它不会找到它。但是,它知道x是C的一个实例,所以它将在C .__ dict__中查找,找到它,并调用m作为第一个参数。

所以vars(x)和dir(x)之间的区别是,dir(x)在x的类(及其基础)中查找可以访问的属性的额外工作,而不仅仅是存储在x自己的符号表。在上面的例子中,vars(x)返回一个空字典,因为x没有实例变量。然而,dir(x)返回

 

['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
'__hash__', '__init__', '__module__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'm']

 

 


 【原理解释】vars(self)的响应缺少属性

我已经定义了一个类,并且需要将其大多数属性移交给函数进行处理。因此,我认为与其创建一个巨大的混乱并命名它们,不如将其processSomething(vars(self))交给我,然后交出带有所有属性和值的漂亮字典。

但是我发现几乎所有的属性都缺失了。

我已停止调试器中的代码并进行了一些测试:

<span style="color:#333333"><code>>>> vars(self).keys()
dict_keys(['date_expire', 'tokenUrl', 'settings', 'requestSession', 'ttlDays'])</code></span>

这些是5。虽然我希望大约有20个属性,但根据__slots__我在这里的课程定义:

<span style="color:#333333"><code>__slots__ = (
    'token',        # the invitation-token to load the username with
    'request',      # the http-request which is currently being serviced
    'inhibit',      
     #... many lines deleted
    'templateObj',  # an instance of myMail.template.Template
    'emailBody',    # what will be sent via SMTP
    'warningsAr',   # messages for the client
)</code></span>

我可以在调试器窗口中看到属性,并且可以直接访问它们。我已经阅读了vars()手册,找不到任何开关。有趣的是,dir(self)显示所有属性名称,但不显示任何值。所以我不能使用它。但是我认为vars和dir应该显示相同吗?

我想我会建立一个解决方法,但我真的很想了解这里发生的事情。你能帮忙吗?

 

解决方案


vars()返回__dict__实例的名称空间。但是__slots__属性未存储在__dict__名称空间中。这就是他们的重点。

相反,Python在实例存储结构中为每个值创建专用的指针槽,并在类对象上使用描述符来检索这些值。因此inst.attr被翻译为type(inst).attr.__get__(inst)返回该值。

__slots__文档中

__slots__声明采用一系列实例变量,并且在每个实例中仅保留足够的空间来容纳每个变量的值。因为__dict__未为每个实例创建空间,所以节省了空间。

[...]

请注意,__dict__ 无论如何拥有一个通常是一个标志,您忘记使用子类__slots__的属性,或者您从不使用__slots__自身的基本Python类继承而来。正确使用会__slots__导致实例中没有__dict__名称空间;他们的目标是通过避免字典所需的稀疏哈希表来减少内存(这会浪费空间)。

再次从文档中:

  • 从没有的类继承时,该类__slots____dict__属性将始终可访问,因此__slots__子类中的定义是没有意义的。

[...]

  • __slots__声明的动作仅限于定义它的类。结果,子类将具有一个,__dict__除非它们也定义__slots__(该子类只能包含任何其他插槽的名称)。

如果要列出所有可用的实例字段,则必须包括__slots__该类的枚举,而不仅仅是看vars()

<span style="color:#333333"><code>from itertools import chain

def slots_for_instance(inst):
    def _slots_for_class(c):
        slots = getattr(c, '__slots__', ())
        if isinstance(slots, str):
            # __slots__ can be a string naming a single attribute
            slots = (slots,)
    return set(chain.from_iterable(
        getattr(_slots_for_class(c) for c in type(inst).__mro__))</code></span>

 

 


vars()等价于.__ dict __

#vars()
#说明:返回对象object的属性和属性值的字典对象
‘’’
vars(…)
vars([object]) -> dictionary
dictionary:字典对象
Without arguments, equivalent to locals().
With an argument, equivalent to object.dict.

class My():
    'Test'
    def __init__(self,name):
        self.name=name

    def test(self):
        print self.name

        
vars(My)#返回一个字典对象,他的功能其实和  My.__dict__  很像


    '''

vars()等价于.__ dict __

vars(My)
Out[11]: 
mappingproxy({'__dict__': <attribute '__dict__' of 'My' objects>,
              '__doc__': 'Test',
              '__init__': <function __main__.My.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'My' objects>,
              'test': <function __main__.My.test>})

My.__dict__
Out[12]: 
mappingproxy({'__dict__': <attribute '__dict__' of 'My' objects>,
              '__doc__': 'Test',
              '__init__': <function __main__.My.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'My' objects>,
              'test': <function __main__.My.test>})

循环按照key value输出

for key,value in vars(My).items():
    print (key,':',value)
    '''
    test : <function test at 0x02112C70>----test函数
    __module__ : __main__
    __doc__ : Test
    __init__ : <function __init__ at 0x01E42B70>----构造函数

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值