注册 登录
  • 欢迎访问"运维那点事",推荐使用Google浏览器访问,可以扫码关注本站的"微信公众号"。
  • 如果您觉得本站对你有帮助,那么可以扫码捐助以帮助本站更好地发展。

Python类和实例

Python编程 彭东稳 286次浏览 未收录 0个评论

类和实例

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是创建实例的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。各个实例拥有的数据都互相独立,互不影响。

以Student类为例,在Python中,定义类是通过class关键字:

第一行:class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

第二行:用来定义注释信息的。

第三行:用来定义类的属性(类中的变量叫属性)。

第四行:用来定义方法(类中的函数叫方法),method方法的第一个参数永远是self,表示创建的实例本身。因此,在method方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

第五行:定义实例属性(只有将类实例化为实例时此定义才有效)。

定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的:

可以看到,变量instance指向的就是一个Student的实例,后面的0x10fde39e8是内存地址,每个object的地址都不一样,而Student本身则是一个类。

类实例化之后,就可以通过点操作符访问实例的属性或者调用实例的方法。调用类属性,也就是类中定义的变量(公共的):

然后可以调用类中的方法,也就是函数:

可以发现,当我调用实例方法的时候,对于self参数是忽略的,上面也说了方法的第一个参数永远是self,表示创建的实例本身,会由解释器自动传入。

调用实例属性,在instance.method调用之前,Student()类不会把name属性附加到实例name上的。

在方法部分,通过定义一个特殊的 __init__ 方法,在创建实例的时候,就把name,score等属性绑上去:

__init__ 方法是用来初始化属性的函数,其第一个参数永远是self,表示创建的实例本身,因此,在 __init__ 方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。其实在类内部默认有一个 __new__ 函数,才是类调用的第一个方法,是真正用来创建实例的,其没有属性,会变成self传递给 __init__ 函数, __init__ 函数会对创建出来的实例做初始化工作。但是通常 __new__ 函数不需要写,除非要改变默认创建实例的行为才需要修改,这也就是元编程了。

另外当没有显示地定义 __init__ 方法时,会使用默认的 __init__ 方法,默认 __init__ 方法如下:

有了 __init__ 方法,在创建实例的时候,就不能传入空的参数了,必须传入与 __init__ 方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

由以上部分可以发现,类体可以包含:声明语句、类成员定义、数据属性、方法等。类中的函数和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数和关键字参数等。

实例属性和类属性

由于Python是动态语言,根据类创建的实例可以动态增减属性。给实例绑定属性的方法是通过实例变量,或者通过self变量:

对Student类可以做如下操作:

但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。为了达到限制的目的,Python允许在定义类的时候,定义一个特殊的 __slots__ 变量,来限制该类实例能添加的属性:

然后,我们试试:

由于’score’没有被放到 __slots__ 中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。

使用 __slots__ 要注意, __slots__ 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义 __slots__ ,这样,子类实例允许定义的属性就是自身的 __slots__ 加上父类的 __slots__ 。

再来说一下作用域,定义一个类:

实例化一下:

我们可以看出两个实例是不同的命名空间,也就是说不同实例之间的操作是不会影响到另外一个实例的变量。

但是,如果类本身需要绑定一个属性呢?可以直接在class中定义属性,这种属性是类属性,归类所有:

当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。来测试一下:

从上面的例子可以看出,在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

对于类属性,所有实例共享。

可以看出两个实例初始化时引用同一个对象,所以当其中一个实例属性发生新的赋值时,Python会创建一个新的对象引用,此时会看见两个实例属性就不同了。

当类属性发生改变时,所有实例属性都会改变(已经重新被赋值过的实例属性不会改变):

为了更好地验证,我们把类属性变成一个列表试试看:

可以发现d1实例追加一个元素后,d2实例也有这个元素。由此看出两个实例都是引用同一个对象,由于d1属性没有发生新的赋值,所以引用对象没有发生变化。

实例方法和类方法及静态方法

通过上面的演示,基本明白了类与实例,以及类属性和实例属性了。对于类的方法,其作用域是类的,如下演示:

实例化一下:

修改实例方法:

方法的作用域都属于类级,可以看出当把类的方法改变之后,所有实例都改变了。另外这种改变方法有一个术语叫“monkey patch”,也是有安全风险的,比如一些黑客可以通过植入代码来改变你的内部函数;好处就是可以在不动第三方库源码的情况下,通过这种方法,可以改变一些内部实现了。

另外类中具体这个方法是实例方法、类方法、或者静态方法,都由第一个参数决定。当第一个参数是实例的时候,就属于实例方法;当第一个参数是类的时候就是类方法;当不要求第一个参数的时候是静态方法。下面介绍类方法。

定义类方法,需要一个内置的@classmethod装饰器,它会向方法传递一个参数,传递的是类本身:

测试类方法和实例方法:

实例方法和类方法的区别在于传入的第一个参数,实例方法会自动传入实例本身作为第一个参数,而类方法会自动传入当前类。

接下来再增加一个静态方法看看:

静态方法的作用不大,一般就是用来做组织代码用的,可以把一批方法归纳到一个命名空间。

可以看出,实例无法调用静态方法,而类可以。当我们用实例调用方法的时候,总是会传入一个参数,要么是实例本身,要么是它的类。

虽然通常静态方法都只是类会调用,但是如果想让实例可以调用静态方法,可以使用@staticmethod装饰器装饰一下(一般都会加上这个装饰器,标明一下这是一个静态变量),当我们实例调用静态方法时,@staticmethod会把第一个参数去掉。

实例调用静态方法:

总结:方法的作用域都属于类级,可以看出当把类的方法改变之后,所有实例都改变了。另外具体这个方法是实例方法、类方法、或者静态方法,都由第一个参数决定。当第一个参数是实例的时候,就属于实例方法;当第一个参数是类的时候就是类方法;当不要求第一个参数的时候是静态方法。静态方法用的一般不多,可以被普通函数或模块替代,而类方法和实例方法一般用的挺多。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (1)or分享 (0)
关于作者:

您必须 登录 才能发表评论!