通常,方法通过右绑定方式调用:
x.f()
在 MyClass 示例中,这会返回字符串 'hello world'.然而,也不是一定要直接调用方法. x.f 是一个方法对象,它可以存储起来以后调用.例如:
xf = x.f
while True:
print(xf())
会不断的打印 hello world.
调用方法时发生了什么?你可能注意到调用 x.f() 时没有引用前面标出的变量,尽管在 f() 的函数定义中指明了一个参数.这个参数怎么了?事实上如果函数调用中缺少参数,Python 会抛出异常--甚至这个参数实际上没什么用......
实际上,你可能已经猜到了答案:方法的特别之处在于实例对象作为函数的第一个参数传给了函数.在我们的例子中,调用 x.f() 相当于 MyClass.f(x) .通常,以 n 个参数的列表去调用一个方法就相当于将方法的对象插入到参数列表的最前面后,以这个列表去调用相应的函数.
如果你还是不理解方法的工作原理,了解一下它的实现也许有帮助.引用非数据属性的实例属性时,会搜索它的类.如果这个命名确认为一个有效的函数对象类属性,就会将实例对象和函数对象封装进一个抽象对象:这就是方法对象.以一个参数列表调用方法对象时,它被重新拆封,用实例对象和原始的参数列表构造一个新的参数列表,然后函数对象调用这个新的参数列表.
众所周知,Python是一门面向对象的语言,在Python无论是数值、字符串、函数亦或是类型、类,都是对象.
对象是在 堆 上分配的结构,我们定义的所有变量、函数等,都存储于堆内存,而变量名、函数名则是一个存储于 栈 中、指向堆中具体结构的引用.
要想深入学习Python,首先需要知道Python对象的定义.
我们通常说的Python都是指CPython,底层由C语言实现,源码地址: cpython [GitHub]
Python对象的定义位于 Include/object.h ,是一个名为 PyObject 的结构体:
Python中的所有对象都继承自PyObejct,PyObject包含一个用于垃圾回收的双向链表,一个引用计数变量 ob_refcnt 和 一个类型对象指针 ob_type
从PyObejct的注释中,我们可以看到这样一句:每个指向 可变大小Python对象 的指针也可以转换为 PyVarObject* (可变大小的Python对象会在下文中解释). PyVarObejct 就是在PyObject的基础上多了一个 ob_size 字段,用于存储元素个数:
在PyObject结构中,还有一个类型对象指针 ob_type ,用于表示Python对象是什么类型,定义Python对象类型的是一个 PyTypeObject 接口体
实际定义是位于 Include/cpython/object.h 的 _typeobject :
在这个类型对象中,不仅包含了对象的类型,还包含了如分配内存大小、对象标准操作等信息,主要分为:
以Python中的 int类型 为例,int类型对象的定义如下:
从PyObject的定义中我们知道,每个对象的 ob_type 都要指向一个具体的类型对象,比如一个数值型对象 100 ,它的ob_type会指向 int类型对象PyLong_Type .
PyTypeObject结构体第一行是一个PyObject_VAR_HEAD宏,查看宏定义可知PyTypeObject是一个变长对象
也就是说,归根结底 类型对象也是一个对象 ,也有ob_type属性,那 PyLong_Type 的 ob_type 是什么呢?
回到PyLong_Type的定义,第一行 PyVarObject_HEAD_INIT(PyType_Type, 0) ,查看对应的宏定义
由以上关系可以知道, PyVarObject_HEAD_INIT(PyType_Type, 0) = { { _PyObject_EXTRA_INIT 1, PyType_Type } 0} ,将其代入 PyObject_VAR_HEAD ,得到一个变长对象:
这样看就很明确了,PyLong_Type的类型就是PyType_Typ,同理可知, Python类型对象的类型就是PyType_Type ,而 PyType_Type对象的类型是它本身
从上述内容中,我们知道了对象和对象类型的定义,那么根据定义,对象可以有以下两种分类
Python对象定义有 PyObject 和 PyVarObject ,所以呢,根据对象大小是否可变的区别,Python对象可以划分为 可变对象(变长对象) 和 不可变对象(定长对象)
原本的对象a大小并没有改变,只是s引用的对象改变了.这里的对象a、对象b就是定长对象
可以看到,变量l仍然指向对象a,只是对象a的内容发生了改变,数据量变大了.这里的对象a就是变长对象
由于存在以上特性,所以使用这两种对象还会带来一种区别:
此外,对于 字符串 对象,Python还有一套内存复用机制,如果两个字符串变量值相同,那它们将共用同一个对象:
按照Python数据类型,对象可分为以下几类:
Python创建对象有两种方式,泛型API和和类型相关的API
这类API通常以 PyObject_xxx 的形式命名,可以应用在任意Python对象上,如:
使用 PyObjecg_New 创建一个数值型对象:
这类API通常只能作用于一种类型的对象上,如:
使用 PyLong_FromLong 创建一个数值型对象:
在我们使用Python声明变量的时候,并不需要为变量指派类型,在给变量赋值的时候,可以赋值任意类型数据,如:
从Python对象的定义我们已经可以知晓造成这个特点的原因了,Python创建对象时,会分配内存进行初始化,然后Python内部通过 PyObject* 变量来维护这个对象,所以在Python内部各函数直接传递的都是一种泛型指针 PyObject* ,这个指针所指向的对象类型是不固定的,只能通过所指对象的 ob_type 属性动态进行判断,而Python正是通过 ob_type 实现了多态机制
Python在管理维护对象时,通过引用计数来判断内存中的对象是否需要被销毁,Python中所有事物都是对象,所有对象都有引用计数 ob_refcnt .
当一个对象的引用计数减少到0之后,Python将会释放该对象所占用的内存和系统资源.
但这并不意味着最终一定会释放内存空间,因为频繁申请释放内存会大大降低Python的执行效率,所以呢Python中采用了内存对象池的技术,是的对象释放的空间会还给内存池,而不是直接释放,后续需要申请空间时,优先从内存对象池中获取.
python中函数和方法的区别:
首先,从分类的角度来分析.
(1)函数的分类:
内置函数:python内嵌的一些函数.
匿名函数:一行代码实现一个函数功能.
递归函数
自定义函数:根据自己的需求,来进行定义函数.
普通方法:直接用self调用的方法.
私有方法:__函数名,只能在类中被调用的方法.
属性方法:@property,将方法伪装成为属性,让代码看起来更合理.
类方法:通过类名的调用去操作公共模板中的属性和方法.
静态方法:不用传入类空间、对象的方法, 作用是保证代码的一致性,规范性,可以完全独立类外的一个方法,但是为了代码的一致性统一的放到某个模块(py文件)中.
其次,从作用域的角度来分析:
(1)函数作用域:从函数调用开始至函数执行完成,返回给调用者后,在执行过程中开辟的空间会自动释放,也就是说函数执行完成后,函数体内部通过赋值等方式修改变量的值不会保留,会随着返回给调用者后,开辟的空间会自动释放.
最后,调用的方式不同.
(1)函数:通过"函数名()"的方式进行调用.
第一段:实例方法,类方法,静态方法
我们首先写一个类,里面包含这三种方法.
可以看到,我们用到了两个装饰器.
我们用类和实例分别调用下类方法
我们用类和实例分别调用下静态方法
静态方法其实就是把一个普通的函数写在类里,与直接在外层写一个函数是一样的,本质上是一个函数.
为了方便理解,我们分别打印下这些方法的类型
通过type()查看对象是方法还是函数
此外,还可以通过inspect模块判断某个对象是否是某种类型,返回布尔值.
用法
小Tips:概念理解
直接def定义的,我们叫做函数
把函数放到类里,我们叫做方法
方法可以通过装饰器staticmethod转为(放在方法里的)函数
继承
一个类继承另一个类时,会自动获得另一个类的所有属性和方法,被继承的类称之为父类,新类称为子类.子类拥有父类所有的属性和方法,并且可以定义自己的属性和方法
我们以上边的Rectangle类为父类来试一下
①.)完全继承
可以看到,子类完全继承父类后,可以直接调用父类的所有方法.
部分继承:继承父类后,修改父类的同名方法
我们试一下,Square继承Rectangle后,修改__init__()方法
在保留父类中某个方法的代码同时,对方法进行拓展
可以在方法中加入"super().方法名"来实现
在Python中,对这两个东西有明确的规定:
函数function —— A series of statements which returns some value to a caller. It can also be passed zero or more arguments which may be used in the execution of the body.
方法method —— A function which is defined inside a class body. If called as an attribute of an instance of that class, the method will get the instance object as its first argument (which is usually called self).
从定义的角度上看,我们知道函数(function)就相当于一个数学公式,它理论上不与其它东西关系,它只需要相关的参数就可以.所以普通的在module中定义的称谓函数是很有道理的.
那么方法的意思就很明确了,它是与某个对象相互关联的,也就是说它的实现与某个对象有关联关系.这就是方法.虽然它的定义方式和函数是一样的.也就是说,在Class定义的函数就是方法.
从上面的角度看似乎很有道理.
def fun():
pass
type(fun)
class 'function' #没有问题
class Cla():
@classmethod
def fun1(cls):
@staticmethod
i=Cla()
Cla.fun.__class__
class 'function' #为什么还是函数
i.fun.__class__ #这个还像话
class 'method'
type(Cla.fun1)
class 'method' #这里又是方法
type(i.fun1)
class 'method'#这里仍然是方法
class 'function' #这里却是函数
class 'function'#这里却是函数
事实上,上面的结果是可以解释的:
①.,普通方法(老版中直接就是"instancemethod")在module中与在Class中定义的普通函数,从其本身而言是没有什么区别的,他们都是对象函数属性. 之所以会被说在Class中的定义的函数被称为方法,是因为它本来就是面向将来的实例对象的,其实他们就是实例方法,这些方法是与实例相联系的(从实例出发访问该函数会自动赋值).所以你从Class访问仍然是一个函数
这样看来上面的定义可以改改了:
函数的定义自然不变.
方法的定义可以是这样的,与某个对象进行绑定使用的函数.注意哦.绑定不是指" . "这个符号,这个符号说实在的只有域名的作用.绑定今天这一节是指,会默认赋值该绑定的对象.
类,简单来说就是一个函数的集合,在这个集合里面你定义了很多个函数;方法,其实就是你定义的这些函数.在下面的例子中Class Plus就是一个类,嵌套在这个类里面的两个函数就是所谓的方法,但是__init__只是用于初始化这个类,所以不算是方法.而get_result这个函数就是一个方法了.
举个例子:
Class Plus:
def __init__(self, a,b)
self.a = a
self.b = b
def get_result(self)
return self.a + self.b