在研究一些python项目的源码时经常会看到一些特殊的方法和变量,比如两边双下划线__init__定义的方法,还有一些类中会有以双下划线__开头定义的变量,以及以单下划线_开头的定义的变量。这里对目前经常遇到的做一个记录

__method__:在class中有一些以双下划线开头和结尾的函数被称为特殊方法(Magic Method),更具体可以参考 官方文档

顺便说一下访问限制。如下(限制其实并不严格,主要靠自觉)

__:在类内部如果使用双下划线开头定义变量,那么这个变量就变成了私有变量(private),即这个变量只有在内部访问(并不是绝对控制,可以通过其他方法访问)

_:在类内部如果使用_开头定义变量,其实表示建议(表示可以从外部访问但最好不要访问)当成私有变量

特殊方法(Magic Method)

常用的内置方法简述如下

object.__new__(cls[, ...])

Python3官方文档

__new__是一个静态方法用于创建对象并返回对象,当返回对象时会自动调用__init__进行初始化。第一个参数是cls表示对象本身,其实self就是__new__方法的返回值

object.__init__(self[, ...])

Python3官方文档

创建实例后由__new__()调用,但在返回给调用方之前调用。用于初始化实例,其中self表示实例本身

object.__str__(self)

Python3官方文档

当使用内置函数print()或format()时被调用,返回值只能是str.__str__ 方法默认调用了 __repr__方法

object.__dir__(self)

Python3官方文档

用于列出该对象内部的所有属性(包括方法)名,该方法将会返回包含所有属性(方法)名的序列。被dir(object)调用,调用会将序列返回为一个经过排序的列表

object.__call__(self[, args...])

Python3官方文档

将一个类实例要变成一个可调用对象,当实例被调用时触发。x() = x.__call__() 例子:flask源码请求入口、django源码请求入口

object.__repr__(self)

Python3官方文档

__str__方法类似__repr__它们都是一个“自我描述”的方法,不过repr偏向于程序员调试(应该更多的表示出一个对象来源的类以及继承关系)。

object.__setattr__(self, name, value)

Python3官方文档

试图分配属性时调用。这被调用而不是正常机制(即,将值存储在实例字典中)。name是属性名,value是要分配给它的值。app.abc = ‘mvalue’

object.__getattr__(self, name)

Python3官方文档

如果name被访问,同时它不存在的时候,此方法被调用。app.abc

object.__delattr__(self, name)

Python3官方文档

如果要删除name,这个方法就被调用

__setitem__

Python3官方文档

设置给定键的值。app[‘abc’]

__getitem__

Python3官方文档

返回键对应的值。app[‘abc’] = ‘mvalue’

__delitem__

Python3官方文档

删除给定键对应的元素

特殊属性

__name__

如果当前程序运行在当前模块中,那么__name__就是__main__,如果是被调用,这个模块中的__name__就是模块名

1
2
3
4
def gourds():
    print('Py name is %s' % __name__)
gourds()
#文件file1一内直接执行 结果是Py name is __main__
1
2
3
import file1
file1.gourds()
#调用执行,结果是Py name is file1

__all__

如果一个模块定义了__all__属性,则当被导入时,只有__all__内指定的属性、方法、类可被导入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
__all__ = ('cls_test_1', 'fun_test_2')
class cls_test_1():
    def __init__(self):
        print('cls_test_1')
class cls_test_2():
    def __init__(self):
        print('cls_test_2')
def fun_test_1():
    print('fun_test_1')
def fun_test_2():
    print('fun_test_2')

如上,如果通过from调用上面的模块,则只有__all__里面定义的方法和类可以使用。如果使用其他未允许的,就会触发NameError: name 'xxx' is not defined

__slots__

__all__不同,__slots__可以用来限制类中实例的属性(这个限制只能限制当前类的实例,对子类没有影响)

1
2
3
4
5
6
7
8
class test_slots():
    __slots__ = ('ta', 'tb')

x = test_slots()
x.ta = 'mta'
x.tb = 'mtb'
print(x.ta)
print(x.tb)

如果视图设置不被允许的属性如x.tc,就会抛出AttributeError: 'test_slots' object has no attribute 'tc',这个在Flask源码中的local对象有使用

__mro__

Python中每一个有父类的类都有这个属性,值是一个tuble。表示方法解析时的对象查找顺序: 越靠前的优先级越高

1
2
3
4
class gourds(object):
    def foo():
        return 'hello'
print(gourds.__mro__) #(<class '__main__.gourds'>, <class 'object'>)

扩展问题

1. 什么是方法什么是函数

先扔下概念

function —— A series of statements which returns some value toa caller. It can also be passed zero or more arguments which may beused in the execution of the body

method —— A function which is defined inside a class body. Ifcalled as an attribute of an instance of that class, the methodwill get the instance object as its first argument (which isusually called self)

  • 函数是Python中的可调用对象,即可以使用call运算符调用
  • 方法是类中的函数。

2. 函数调用时加括号和不加括号的区别

不加括号表示调用函数本身,加括号就表示调用函数执行结果

1
2
3
4
5
def foo():
    return 'hello'

print(foo) #<function foo at 0x101fcee18>
print(foo()) #hello

3. super函数和mro方法的区别

super原理:通过mro()方法获取到实例的方法解析顺序,在mro顺序中返回当前类的下一个类

具体可以参考MRO 和 super