TOC

面向对象

    类是一个抽象的概念,对于类需要知道的是,我们写的东西,不过是属性和操作的集合而已,它是抽象的,如下就是一个类的定义语法,必须使用class关键字,类名要求每个英文单词的首字母大写大驼峰命名方式,类定义完成之后就产生了一个类对象,绑定到类表示服ClassName上;
class ClassName:
    '注释'
    语句块

类对象

    对于类来讲,class关键字后面有一个标识符,这个标识符就是这个类对象的名字,其中类对象中有类方法和类变量,其中类方法可以称之为类方法属性,类变量可以称之为类的数据属性,类属性可以通过"."的方式来访问,如下示例。
    类属性其实就是在类中定义的类变量,类方法其实就是类中的函数,而这个类变量和类方法都局限在这个类当中,类创建完之后就会创建一个类对象,这个类对象就和class关键字后面的标识符建立了关联关系,以后通过标识符就可以访问到类对象,访问到类对象之后就可以访问到内中的属性。
class ClassName: # 类对象
    name='cce' # 类变量/类数据属性
    def foo(self): # 类方法/类方法属性
        return 'foo method'
print(ClassName.name) # cce
print(ClassName.foo) # <function ClassName.foo at 0x102e7d9d8>
# 实例化(在类对象名称后面加上一个括号,类似运行函数一样,就是调用类的实例化方法)
instance = ClassName() # 创建一个ClassName的实例

面向对象基础术语

    面向对象中有很多常见的一些术语,这些术语无论在任何语言中都是相同的,只要它有面向对象语法,学好面向对象也是必不可少的一些基础概念,如下;
类对象:指的是类定义之后,会在内存中生成一个类对象;
类变量:即,类中的变量,又称之为类数据属性;
类方法:即,类中的函数,又称之为类方法属性,一般它最少有一个参数,第一个形参一般都是self,而self则指代为当前实例本身,也就是通过类创建出来的具体的实例,其实就是其他语言的this指针;
类属性:类属性包括类方法和类变量,在上述例子中,name为类数据属性,foo为类方法属性,但它们都是类属性;
实例:类就像是一个模版,是一个抽象的概念,实例化就是用类这个模版创建出真正的实例,也就是具体的对象,实例化分两个步骤,第一部分就是实例化,而第二部分则是初始化,实例化出来的实体,看起来很像,但是实际上它们都是不同的个体,如,1、2、3都是int类的实例;

实例初始化

    Python实例化实际上是分两个步骤,第一个步骤就是实例化,用类的模版创出一个实例,那么实例创造出来了,后面就会做一些出厂设置操作,这个出厂设置,就是初始化,做一些基本的设置,这就是实例化的两段。
    初始化就是调用类对象中的__init__方法,这个方法我们称之为魔术方法,这个方法第一个形参必须留给self,其他参数随意,也就是说也有其他参数,但是第一参数必须留给self,self指的是实例本身,因为类可以创造很多实例,所以利用self来指代当前正在操作的实例。
    需要知道的是__init__实例初始化方法是在后台隐式调用的,无需手动调用__init__方法去进行初始化实例。
class Person:
    def __init__(self, name):  # 初始化->出厂配置,即对生成的实例进行属性填充
        self.name = name  # self指的是实例本身,self.name就是表示给实例添加一个name属性,值为__init__方法的name值

p1 = Person('cce')
print(p1.name) # cce
p2 = Person('cfj')
print(p1.name) # cfj 
print(Person.name) # type object 'Person' has no attribute 'name'    name不属于类,值属于实例

Person('cce') # 会调用__init__方法
print(Person) # 不会调用__init__方法

实例方法

    在Python的面向对象中,其实并没有方法这个东西,但是为了区分函数和数据,所以就有了方法这个代名词,所有的东西都保存在属性里面,所谓的调用方法其实是类里面的一个同名属性指向了一个函数(方法),返回的是函数的引用,再用函数加上括号的方式就可以调用它;
    当实例创建完成之后,就可以利用实例来调用该实例自身的方法属性(从类实例化来的方法属性)来完成特定的操作,如下面的showname,其实showname也是类的属性,可以直接用类来进行调用,但是如果涉及到实例的属性则会报错,所以当使用Person.showname()的时候就会报错,因为Person.showname()需要传入一个必须的self参数,这个self也就是“实例”,所以要使用实例方法时必须先有实例。
    当我们使用Person.showname()的时候是没有传入实例的,从而报错,所以我们要使用showname()必须先构造一个实例,并且还要将实例传入进去才行。
class Person:
    def __init__(self, name):  # 初始化->出厂配置,即生成的实例进行属性填充
        self.name = name  # self指的是实例本身,self.name就是表示给实例添加一个name属性,值为__init__方法的name值
    def showname(self): #
        return self.name

print(Person.showname()) # showname() missing 1 required positional argument: 'self'
Person.showname(Person('cce')) # cce
# 同时Python还提供了一种语法,当使用实例直接调用showname()时,会自动将实例传入进去,并且变成第一形参
p1 = Person('cfj')
print(p1.showname()) # cfj

类/实例变量

    类变量,即类数据属性,它定义在类内部方法外部,也就是下例的age,它们都属于类属性,类属性(包括数据属性和方法属性)是所有实例共享的,一旦改变,那么利用此类创建的所有实例会随之改变,而定义在类的方法中的属性,如果以self开头,则为实例变量,即实例方法属性。
    需要注意的是,类变量也可以通过实例来访问,如果使用下述例子使用实例访问age,如果在实例中没有则会再类属性里面去找,有个优先级关系,实例自身的数据属性优先级最高,可以将它理解为全局变量和局部变量的意思,局部变量会覆盖全局变量。
class Person:
    age = 18 # 类变量
    def __init__(self, name):
        self.name = name # 实例变量

print(Person.age) # 18
p1 = Person('cce')
print(p1.age) # 18

属性优先级

    上面说过,属性也是有优先级的,而类的数据属性则是全局的,一旦改变,那么使用该类创建的实例也将随之全部改变,而实例的数据属性则的独立的,改变数据属性的值则只针对当前修改的实例生效,不影响其他同类实例,总结一句话,类属性改变,影响全局,实例属性改变影响当前实例,一个全局,一个局部,如下;
class Person:
    age = 18  # 类变量
    def __init__(self, name):
        self.name = name  # 实例变量

p1 = Person('cce')
Person.city = 'wuhan' # 新增属性将对所有已创建和未创建的实例生效
print(p1.city, Person.city)  # wuhan wuhan
p2 = Person('cfj')
print(p2.age)  # 18
Person.age = 19 # 一旦修改了类的数据属性,那么之前创建的p1、p2和未来创建p3都会随之改变;
p3 = Person('csw')
print(p2.age, p1.age, p3.age)  # 19 19 19

类的特殊方法

    Python面向对象中有很多特殊的方法,它们无需我们手动定义,Python内部会自己将其补上,如下,这些特殊方法主要是描述这个类的大概的一些情况,它们属于类,但不一定属于对象。
__name__:类中内置的一个特殊属性,返回当前类的名字;
__doc__:类中内置的一个特殊属性,返回当前类说明文档;
__init__:初始化函数,第一参数必须为self,用来对实例作一些初始化配置;
__class__:对象类型,等价于type;
__dict__:对象的属性字典;
__qualname__:类的限定名;

对象属性字典

    使用__dict__方法会返回一个属性字典,但是需要注意的是,在Python的面向对象里面,使用__dict__会返回当前类或者当前实例的属性字典,是当前类或者当前实例,那么我们如果在实例中使用['property_name']字典的方式来访问,可能是不存在的,因为这个property_name可能是类的,而不是实例自身的。
    而我们使用对象的方式访问就可以直接访问到,这是因为实例访问属性时,如果实例自己没有,会去自己的父类找,并且返回,如下。
class Person:
    age = 18  # 类变量
    def __init__(self, name):
        self.name = name  # 实例变量

p1 = Person('cce')
print(p1.__dict__['age'])  # KeyError: 'city'  访问报错
print(p1.age)  # wuhan 

总结

    类的属性,不论是数据属性还是方法属性,可以使用类来访问,也可以通过实例来访问,但是实例内部的不论是数据属性还是其他属性只能实例自己才能访问。
    类或实例都可以动态增加属性,因为赋值即定义,通过实例.__dict__['变量名‘]或实例.变量名都可以访问到实例自己的属性,但是需要注意的是,这两者有本质区别,前者是使用字典的方式查找,如果字典中没有就没有,后者是对象的方式查找,如果没有会向父类去寻找。

实例属性查找顺序

    实例使用“.”来访问属性,首先会找自己的__dict__,如果没有,会通过__class__属性找到自己所属的类,然后会再去类的__dict__里面去找。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注