关于python:Python类内置方法简介

6次阅读

共计 4877 个字符,预计需要花费 13 分钟才能阅读完成。

与其余语言相比,Python 中的类提供了很多双下划线结尾和结尾 __xxx__ 的办法,这些办法是 Python 运行的根底,很多性能背地都是通过调用这些内置办法来实现的。如 len() 函数调用对象的 __len__ 办法;print(obj)函数调用对象的 __str__ 办法,for item in iterable_obj调用对象的 __next____iter__ 办法。

注:因为所有类内置办法都在双下划线结尾和结尾,下文形容中,为形容简洁,在无歧义状况下,局部形容中会去掉办法前后双下划线。

__new____init__

new 和 init 这两个办法很容易混同,平时定义类时,通常应用的都是 init 办法,很少用到 new 办法,但他们是有着截然不同的性能的。
new 是类办法,用于创立实例对象,办法必须返回一个对象;而 init 是实例办法,执行实例初始化,在 new 返回的对象中执行。

阐明:IDE 提供的 stub 办法显示 new 是 @staticmethod,但依据办法参数中携带了 cls 参数,集体认为应该是 @classmethod

失常类的实例化过程如下。

class Obj(object):
    def __new__(cls):
        print("__new__ in <Obj>")
        return object.__new__(Obj)
    def __init__(self):
        print("__init__ in <Obj>")
        
obj = Obj()
print(type(obj))

# 执行输入
>> __new__ in <Obj>
>> __init__ in <Obj>
>> <class '__main__.Obj'>

如果咱们在类的 new 办法中,返回成其余类型对下,则最终失去的会是新类型。

class OldObj(object):
    def __new__(cls):
        print("__new__ in <OldObj>")
        return object.__new__(NewObj)
    def __init__(self):
        print("__init__ in <OldObj>")
        
class NewObj(object):
    def __init__(self):
        print("__init__ in <NewObj>")
        
obj = OldObj()
print(type(obj))

# 执行输入
>> __new__ in <OldObj>
>> <class '__main__.NewObj'>

这里有个疑难,为什么 new 执行之后,既没执行 OldObj 的 init 办法,也没执行 NewObj 的 init 办法,有待钻研。

在利用上,能够通过覆写 new 办法,来实现单例模式。

class Singleton(object):
    instance = None
    def __new__(cls):
        if not cls.instance:
            cls.instance = object.__new__(cls)
        return cls.instance
        
s1 = Singleton()
s2 = Singleton()

print(s1 == s2) # 输入:True

__str____repr__

str 和 repr 都返回一个对象的字符串形容,不过两者的用处不同,str 能够了解是给人浏览的,而 repr 是给程序应用的,官网对 repr 的形容如下:

repr(obj, /)
Return the canonical string representation of the object.
For many object types, including most builtins, eval(repr(obj)) == obj.

print(obj)办法调用对象的 str 办法,而交互式 CLI 和调试时,查看对象时返回的是 repr,不过和多状况下程序员把 str 和 repr 设置为一样__str__ == __repr__

__call__

call 办法把一个对象变成为可调用对象,即通过 obj() 间接调用对象时,零碎执行的是对象的 call 办法,实现 call 办法的对象,callable(obj)返回为 True。

class CallObj(object):
    def __call__(self, *args, **kwargs):
        print("__call__")
        
obj = CallObj()
print(callable(obj))
obj() # 调用了对象的__call__办法

利用方面,能够通过 call 语法糖,简化对象的调用;也能够用户实现 call 办法的对象代替基于函数的装璜器,简化代码构造。

__iter____next__

在 Java 等强类型的语言中,对象的性能个性必须通过继承或实现接口来实现,比方可迭代的类,必须继承自 Iterator 或实现 Iterable 接口,并实现相干的办法。而对于动静语言的 Python 来说,它属于 鸭子类型,只有一个类实现了 iter 和 next 办法,它就是一个可迭代对象。

鸭子类型(英语:duck typing)在程序设计中是动静类型的一种格调。在这种格调中,一个对象无效的语义,不是由继承自特定的类或实现特定的接口,而是由 ” 以后办法和属性的汇合 ” 决定。这个概念的名字来源于由詹姆斯·惠特科姆·莱利提出的鸭子测试.
“鸭子测试”能够这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。”

class Iter:
    def __init__(self, max):
        self.max = max
        self.current = 0
 def __iter__(self):
        return self
 def __next__(self):
        if self.current < self.max:
            self.current += 1
 return self.current
        else:
            raise StopIteration("out-of-bounds")
            
for i in Iter(10):
    print(i)

__getitem____setitem____delitem__

这三个办法,次要服务于类 list、dict 类型的数据结构的中括号 [] 数据操作中,包含下标操作、切片操作、Key 值操作等,通过简略测试,发现 Python 作为一个合格的“鸭子类型”语言,相干办法能接管的参数类型比 list、dict 提供的更丰盛,为展现相干办法能够应用的参数,以下示例只对办法参数进行打印,不实现具体性能逻辑:

class OpItem:
    def __getitem__(self, item):
        print("getitem, args:", item)
    def __setitem__(self, key, value):
        print("setitem, args:", key, value)
    def __delitem__(self, key):
        print("delitem, args:", key)
        
item_test = OpItem()

getitem 操作:

# 下标读取操作
item_test[1] # >> getitem, args: 1
# 切片读取操作
item_test[1:10] # >> getitem, args: slice(1, 10, None)
# 带 step 的切片读取操作
item_test[1:10:2] # >> getitem, args: slice(1, 10, 2)
# 通过 key 值读取操作
item_test["str_key"] # >> getitem, args: str_key
# 切片为 key 值,非标操作,办法会忠诚的传递参数
item_test["key1":"key2"] # >> getitem, args: slice('key1', 'key2', None)

setitem 操作:

# 下标赋值操作
item_test[1] = 99 # >> setitem, args: 1 99
# 切片替换操作
item_test[1:2] = [10, 20] # >> setitem, args: slice(1, 2, None) [10, 20]

delitem 操作:

# 下标删除操作
del item_test[5] # >> delitem, args: 5
# 切片删除操作
del item_test[5:10] # >> delitem, args: slice(5, 10, None)

__getattr____setattr____delattr__

这组办法是在框架开发中必备神器。
当拜访对象中不存在的属性时,零碎会去调用对象的 getattr 办法,通过对此办法的解决,能够给零碎凭空发明出原来不反对的性能。如拜访obj.attr1,而 obj 中不存在 attr1 时,会触发 getattr 办法。
setattr 在向对象属性设值时触发,如obj.attr1 = 100;delattr 在删除属性时触发,如del obj.attr1

比方在一个 ORM 的模型中,咱们须要把数据库字段相干的属性保留在对象内的 dict 型字段 fields 中,实现和对象的其余字段隔离,就能够通过以上三个办法来实现。

class User:
    def __init__(self):
        # 保留数据库表映射字段
        self.__dict__['fields'] = dict()
        # 数据库字段列表
        self.__dict__["fields_list"] = ["name", "age", "address"]
    def __setattr__(self, key, value):
        # 实现通过 user.name = xxx 赋值 
        if key in self.fields_list:
            self.__dict__["fields"][key] = value
            return
        self.__dict__[key] = value
    def __getattr__(self, key):
        # 实现通过 user.name 取值
        if key in self.fields_list:
            return self.__dict__["fields"][key]
        return self.__dict__[key]
        
user = User()
user.name = "zhangsan"
print(user.__dict__['fields']['name']) # >> zhangsan

在实现了 getattr 办法的类的外部,对对象属性进行赋值时须要特地留神,很容易引发 getattr 的有限循环。
在 init 中,对象的所有自定义属性都没有初始化,此时如果对属性赋值,会触发调用 setattr 办法,拜访属性则触发 getattr。
所以在类外部,最好通过 self.__dict__对对象属性进行赋值和取值,防止通过点(.)运算符(self.attrself.attr = xxx)引发有限循环。

__getatrribute__

getatrribute 是一个属性拜访拦截器,会拦挡所有对对象属性拜访申请,不论属性是否存在,且具备最优先的拜访查问程序。
对象的属性查找程序如下:
实例的 getattribute → 实例对象字典 → 实例所在类字典 → 实例所在类的父类 (MRO 程序) 字典 → 实例所在类的 getattr → 报错

__enter____exit__

enter 和 exit 能够让对象通过 with 关键字来进行应用,提供进入 with 块前的初始化工作和退出 with 块后的清理工作,罕用于文件和数据库操作中。

class DbConnect:
    def connect(self):
        print("Init and connect to db.")
    def execute(self):
        print("Execute SQL statement.")
    def disconnect(self):
        print("Disconnect from db.")
    def __enter__(self):
        self.connect()
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.disconnect()
        
with DbConnect() as conn:
    conn.execute()

# 输入
# >> Init and connect to db.
# >> Execute SQL statement.
# >> Disconnect from db.
正文完
 0