牢记python对象的操作方式

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 【6月更文挑战第20天】在Python中,`hash()`和`is`帮助确定对象的相等性。`dir()`和`vars()`揭示对象的属性和内部表示,`__slots__`优化内存使用。列表和字典结构有不同的内存和性能特性,字典使用哈希表进行快速访问。

简介

默认情况下,python对象不可比较,除非它们相同。is检查内存中的对象引用,而==对于某些类型(如object),实际上是基于id()比较。

哈希值是数据的唯一标识,相同数据的哈希值总是相同,不同数据的哈希值几乎不可能相同。哈希适用于快速查找,如字典和集合。__hash____eq__方法允许自定义对象的哈希行为。

Toroid托瑞德符号.png

2 hash和id 相等

内置函数 hash 和 id 构成了python对象是否相等的支持默认情况下,python对象是不可比较的,除非它们相同。

A = object()
B = object()
A == B
    False

object也被称为哨兵,因为它们可用于准确检查无法复制的值

    __my_sentinel = object()

   def what_was_passed(value=__my_sentinel):
        if value is __my_sentinel:
            print('Nothing was passed.')
        else:
            print(f'You passed a {value!r}.')
    >>> what_was_passed("abc")
        You passed a 'abc'.
    >>> what_was_passed(object())
        You passed a <object object at 0x0000027AF6AD88C0>.
    >>> what_was_passed()
        Nothing was passed.
    >>> what_was_passed(None)
        You passed a None.

python的比较,我们必须了解is关键字python的 is运算符用于检查两个值 是否引用内存相同的确切对象。

将python对象想象位空间中漂浮的盒子,变量,数组索引等被命名位指向这些对象的箭头

而 object 的行为 == 被定义为比较 id,例如这样的类型东西可覆盖 ==

  class object:
       def __eq__(self, other):
              return self is other

实际实现object 是用 C 编写的

  x = [1, 2, 3]
  y = [1, 2, 3]
  x is y
      False
  x == y
      True

我们还没有看过all或者zip还没有看过,但这一切都是为了确保所有给定的列表索引都是相等的。

同样,集合是无序的,所以即使它们的位置也不重要,只有它们的“存在” Python 有了hashes的概念。

任何一段数据的“散列”指的是一个看起来非常随机的预先计算的值,但它可以用来识别那段数据。

3 哈希及其属性

哈希有两个特定的属性:

相同的数据将始终具有相同的哈希值。

即使是非常轻微地更改数据,也会以完全不同的哈希值返回。

这意味着如果两个值具有相同的哈希值,那么它们很可能 * 也具有相同的值。

比较哈希是检查“存在”的一种非常快速的方法。这是字典和集合用来立即在其中查找值的方法:

            >>> hash(42) == hash(42.0) == hash(42+0j)
        True

不可变的容器对象,例如字符串(字符串是字符串的序列)、元组和frozensets,通过组合它们的项目的哈希来生成它们的哈希。

这允许您通过组合函数来为您的类创建自定义哈希hash函数

class Car:
    def __init__(self, color, wheels=4):
        self.color = color
        self.wheels = wheels

    def __hash__(self):
        return hash((self.color, self.wheels))

4 dir 和 vars 一切都是字典对象

所有内容都存储在字典中,vars方法公开了存储在对象和类的变量 一个函数的 的vars方法

    >>> vars(what_was_passed)
        {}

一个类的vars方法

    c = C(x=3)

    >>> vars(C)
    mappingproxy({'__module__': '__main__', 
    'some_cons': 42, 
    '__init__': <function C.__init_
    _ at 0x0000027AF72F4940>, 
    'mth': <function C.mth at 0x0000027AF72F49D0>, 
    '__dict__': <at
    tribute '__dict__' of 'C' objects>, 
    '__weakref__': <attribute '__weakref__' of 'C' objects>, 
    '__doc__': None})
    >>> vars(c)
    {'x': 3}

    c.__class__  # 来自继承
    <class '__main__.C'>

方法(mth和init)实际上作为函数存储在类的字典中 ,函数本身的代码不会因每个对象而改变,只有传递给它的变量会改变。

>>> class C:
...     def function(self, x):
...         print(f'self={self}, x={x}')

>>> c = C()
>>> C.function(c, 5)
self=<__main__.C object at 0x7f90762461f0>, x=5
>>> c.function(5)
self=<__main__.C object at 0x7f90762461f0>, x=5

5 查看objects的所有属性:

    >>> "__class__" in vars(object)
    True
    >>> vars(object).keys()
    dict_keys(['__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__dela
    ttr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__init__', '__new__
    ', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__',
     '__sizeof__', '__dir__', '__class__', '__doc__'])

“方法解析顺序”是如何工作的。简称 MRO, 这是一个对象继承属性和方法的类的列表。

    >>> class A:
    ...     def __init__(self):
    ...         self.x = 'x'
    ...         self.y = 'y'
    ...
    >>> class B(A):
    ...     def __init__(self):
    ...         self.z = 'z'
    ...
    >>> a = A()
    >>> b = B()
    >>> B.mro()
    [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    >>> dir(b)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
    '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__', '__weakref__', 'x', 'y', 'z']
    >>> set(dir(b)) - set(dir(a))  # all values in dir(b) that are not in dir(a)
    {'z'}
    >>> vars(b).keys()
    dict_keys(['z'])
    >>> set(dir(a)) - set(dir(object))
    {'x', 'y'}
    >>> vars(a).keys()
    dict_keys(['x', 'y'])

因此,每一级继承都会将较新的方法添加到dir列表中,并dir在子类上显示在其方法解析顺序中找到的所有方法。

这就是 Python 在 REPL 中建议方法完成的方式:

class A:
    x = 'x'
class B(A):
    y = 'y'
b = B()
b.   # 两次 tab 键  。???
    b.x  b.y   # 自动

slots 它只允许其中定义的内容 被类实现。 如果对比 go 的接口,可以类型理解,在go接口中如果没有实现,则编译检查报错。 在python中如果实现超过 slots 定义,则报错。

这是 Python 具有的一种奇怪/有趣的行为:

object 默认具有 slots, 也支持自定义内容。

>>> x = object()   
>>> x.f = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'f'
>>> class A:   
...     x = 1
...
>>> a  = A()
>>> a.f = 5

这就是hash用武之地。首先,让我复制我自己的类list和object在我自己的类中显示的行为:

>>> class C:
...     __slots__ = ()
...
>>> c = C()
>>> c.foo = 5
AttributeError: 'C' object has no attribute 'foo'

6 python的存储数据方式

字典将更多使用内存,列表结构,本质上类型python的元组,结构具有固定大小,内存更少。

这两种方式通过两个对象属性 dictslots

通常所有实例属性 self.foo 都存储在dict字典
除非定义 slots属性,这样对象只能具有恒定的指定的属性
定义了 slots将不会加载 dict

如下 list 的内置函数:

 vars(list)
    mappingproxy({'__repr__': <slot wrapper '__repr__' of 'list' objects>, '__hash__': None, '__getattribute__': <slot wrapper '__getattribute__' of 'list' objects>, '__lt__': <slot wrapper '__lt__' of 'list' objects>, ...)

    __repr__:<slot wrapper '__repr__' of 'list' objects>

7 清晰一些的显示 list 结构:

__hash__:None
__getattribute__:<slot wrapper '__getattribute__' of 'list' objects>
__lt__:<slot wrapper '__lt__' of 'list' objects>
__le__:<slot wrapper '__le__' of 'list' objects>
__eq__:<slot wrapper '__eq__' of 'list' objects>
__ne__:<slot wrapper '__ne__' of 'list' objects>
__gt__:<slot wrapper '__gt__' of 'list' objects>
__ge__:<slot wrapper '__ge__' of 'list' objects>
__iter__:<slot wrapper '__iter__' of 'list' objects>
__init__:<slot wrapper '__init__' of 'list' objects>
__len__:<slot wrapper '__len__' of 'list' objects>
__getitem__:<method '__getitem__' of 'list' objects>
__setitem__:<slot wrapper '__setitem__' of 'list' objects>
__delitem__:<slot wrapper '__delitem__' of 'list' objects>
__add__:<slot wrapper '__add__' of 'list' objects>
__mul__:<slot wrapper '__mul__' of 'list' objects>
__rmul__:<slot wrapper '__rmul__' of 'list' objects>
__contains__:<slot wrapper '__contains__' of 'list' objects>
__iadd__:<slot wrapper '__iadd__' of 'list' objects>
__imul__:<slot wrapper '__imul__' of 'list' objects>
__new__:<built-in method __new__ of type object at 0x00007FFE87DA1AF0>
__reversed__:<method '__reversed__' of 'list' objects>
__sizeof__:<method '__sizeof__' of 'list' objects>
clear:<method 'clear' of 'list' objects>
copy:<method 'copy' of 'list' objects>
append:<method 'append' of 'list' objects>
insert:<method 'insert' of 'list' objects>
extend:<method 'extend' of 'list' objects>
pop:<method 'pop' of 'list' objects>
remove:<method 'remove' of 'list' objects>
index:<method 'index' of 'list' objects>
count:<method 'count' of 'list' objects>
reverse:<method 'reverse' of 'list' objects>
sort:<method 'sort' of 'list' objects>
__class_getitem__:<method '__class_getitem__' of 'list' objects>
__doc__:Built-in mutable sequence.

8 字典dict 结构:

__repr__:<slot wrapper '__repr__' of 'dict' objects>
__hash__:None
__getattribute__:<slot wrapper '__getattribute__' of 'dict' objects>
__lt__:<slot wrapper '__lt__' of 'dict' objects>
__le__:<slot wrapper '__le__' of 'dict' objects>
__eq__:<slot wrapper '__eq__' of 'dict' objects>
__ne__:<slot wrapper '__ne__' of 'dict' objects>
__gt__:<slot wrapper '__gt__' of 'dict' objects>
__ge__:<slot wrapper '__ge__' of 'dict' objects>
__iter__:<slot wrapper '__iter__' of 'dict' objects>
__init__:<slot wrapper '__init__' of 'dict' objects>
__or__:<slot wrapper '__or__' of 'dict' objects>
__ror__:<slot wrapper '__ror__' of 'dict' objects>
__ior__:<slot wrapper '__ior__' of 'dict' objects>
__len__:<slot wrapper '__len__' of 'dict' objects>
__getitem__:<method '__getitem__' of 'dict' objects>
__setitem__:<slot wrapper '__setitem__' of 'dict' objects>
__delitem__:<slot wrapper '__delitem__' of 'dict' objects>
__contains__:<method '__contains__' of 'dict' objects>
__new__:<built-in method __new__ of type object at 0x00007FFE87D988B0>
__sizeof__:<method '__sizeof__' of 'dict' objects>
get:<method 'get' of 'dict' objects>
setdefault:<method 'setdefault' of 'dict' objects>
pop:<method 'pop' of 'dict' objects>
popitem:<method 'popitem' of 'dict' objects>
keys:<method 'keys' of 'dict' objects>
items:<method 'items' of 'dict' objects>
values:<method 'values' of 'dict' objects>
update:<method 'update' of 'dict' objects>
fromkeys:<method 'fromkeys' of 'dict' objects>
clear:<method 'clear' of 'dict' objects>
copy:<method 'copy' of 'dict' objects>
__reversed__:<method '__reversed__' of 'dict' objects>
__class_getitem__:<method '__class_getitem__' of 'dict' objects>
__doc__:dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

9 小结

在Python中,hash()is帮助确定对象的相等性。dir()vars()揭示对象的属性和内部表示,__slots__优化内存使用。列表和字典结构有不同的内存和性能特性,字典使用哈希表进行快速访问。

目录
相关文章
|
7天前
|
Python
高阶函数如`map`, `filter`, `reduce`和`functools.partial`在Python中用于函数操作
【6月更文挑战第20天】高阶函数如`map`, `filter`, `reduce`和`functools.partial`在Python中用于函数操作。装饰器如`@timer`接收或返回函数,用于扩展功能,如记录执行时间。`timer`装饰器通过包裹函数并计算执行间隙展示时间消耗,如`my_function(2)`执行耗时2秒。
15 3
|
1天前
python+Pycharm+selenium操作浏览器(Chorme或Edge)
python+Pycharm+selenium操作浏览器(Chorme或Edge)
5 1
|
2天前
|
jenkins 持续交付 API
使用Python操作Jenkins的过程详解
Python作为一种简洁、灵活且功能丰富的编程语言,可以与各种API轻松集成,Jenkins的API也不例外。借助于Python中的python-jenkins模块,我们可以轻松地编写脚本来连接到Jenkins服务器,并执行各种操作,如创建、删除、构建Jobs等。这种自动化的方式不仅提高了效率,还使得CI/CD流程更加灵活和可控。
|
7天前
|
算法 Java 程序员
Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。
【6月更文挑战第20天】Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。为解决此问题,Python使用`gc`模块检测并清理循环引用,可通过`gc.collect()`手动回收。此外,Python结合标记清除和分代回收策略,针对不同生命周期的对象优化垃圾回收效率,确保内存有效释放。
13 3
|
7天前
|
SQL Oracle 关系型数据库
Python连接数据库进行数据查询的操作代码
mysql数据库(mariadb) 连接数据库 首先,你需要使用MySQLdb.connect()函数建立与MySQL数据库的连接。你需要提供数据库服务器的地址(host),用户名(user),密码(passwd),以及你想要操作的数据库名称(db)。 创建Cursor对象 一旦建立了数据库连接,你可以使用连接对象的cursor()方法来创建一个cursor对象。这个方法返回一个cursor实例,你可以使用这个实例来执行SQL查询和命令。
|
1天前
|
算法 Java 程序员
Python面相对象的编程
Python面相对象的编程
|
3天前
|
消息中间件 存储 JSON
python 操作RabbitMq详细操作分享
python 操作RabbitMq详细操作分享
11 0
|
3天前
|
存储 NoSQL Redis
Python—操作redis的一些心得
Python—操作redis的一些心得
|
3天前
|
NoSQL Shell MongoDB
python操作MongoDB部分
python操作MongoDB部分
|
4天前
|
分布式计算 DataWorks 大数据
MaxCompute操作报错合集之pyODPS导入python包的时候报错,该怎么办
MaxCompute是阿里云提供的大规模离线数据处理服务,用于大数据分析、挖掘和报表生成等场景。在使用MaxCompute进行数据处理时,可能会遇到各种操作报错。以下是一些常见的MaxCompute操作报错及其可能的原因与解决措施的合集。