欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python 学习笔记-第8讲:类和对象

程序员文章站 2022-05-28 15:50:46
...

一、概念

面向对象的三个特征:
封装,继承和多态

类:具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
类是一种概念,抽象,不代表具体的

class 类名:
属性
方法(是指在类中定义的函数;函数: 在类的外部定义)

# 默认, Python 3
class A:   #新式类,类名后不需要()
    pass

# Python 2
class A(object):  # 必须写明 A 继承自 object
    pass

对象:万物皆对象
对象是具体的
特征(名词),行为(动词)
属性, 方法

类对象支持两种操作:属性引用和实例化。

使用点运算符“.”可以范围类对象的属性和调用类对象的方法obj.name

二、类的构造函数

1. __init__(self):
    系统自动调用的初始化方法,用于对创建的类实例进行初始化,为实例化对象提供初始属性值
class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return "%s的年龄为 %s 岁。)"%(self.name,self.age)

lisi=Person('李四',23) # 在创建类对象时赋予属性值
print(lisi)
输出:
李四的年龄为 23 岁。

如果在类中未定义 __init__(self) 方法,则将默认继承父类的 __init__(self) 方法,在 python 中,类 object 为所有类的基类。

class myClass:
    i=2
    def f(self):
        return self.i**3


x=myClass()
print('x.i=',x.i)
print('x.f()=',x.f())
print('x 为:',x)
print('x 的类型为:',type(x))
print('x 是类 myClass 的实例:', isinstance(x, myClass))
print('x 是类 object 的实例:',isinstance(x,object))
输出:
x.i= 2
x.f()= 8
x 为: <__main__.myClass object at 0x000002C9985C7FD0>
x 的类型为: <class '__main__.myClass'>
x 是类 myClass 的实例: True
x 是类 object 的实例: True
2. __new__(cls) 方法:
    在 __init__(self) 之前调用,用来创建类实例,而且必须有返回值
 
class Person:
    def __new__(cls, name,age):
        print('已调用 __new__,cls 为',cls)
        return object.__new__(cls)    # 也可以使用 return super(Person,cls).__new__(cls)


    def __init__(self, name, age):
        print('已调用 __init__。')
        self.name = name
        self.age = age
    def __str__(self):
        return "%s的年龄为 %s 岁。"%(self.name,self.age)

lisi=Person('李四',23) # 在创建类对象时赋予属性值
print(lisi)
输出:
已调用 __new__,cls 为 <class '__main__.Person'>
已调用 __init__。
李四的年龄为 23 岁。
类的实例化执行逻辑:(引用自 http://python.jobbole.com/86506/)

    1.p = Person(name, age)
    2.首先执行使用name和age参数来执行Person类的__new__方法,这个__new__方法会返回Person类的一个实例(通常情况下是使用         super(Persion, cls).__new__(cls, … …) 这样的方式),
    3.然后利用这个实例来调用类的__init__方法,上一步里面__new__产生的实例也就是 __init__里面的的 self
    所以,__init__ 和 __new__ 最主要的区别在于:
        1.__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被  创建完以后。它是实例级别的方法。
        2.__new__ 通常用于控制生成一个新实例的过程。它是类级别的方法。


3. __str__(self) 方法

    __str__方法和__init__方法类似,都是一些特殊方法,所以前后都有双下划线,它用来返回对象的字符串表达式。

    用print()输出实例对象时,Python会调用实例对象的 __str__()方法。因此,在进行程序调试时,可在 __str__() 方法中定义要返回的输出字符串信息。

def __str__(self):
        return '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second)
>>> time = Time(9, 45)
>>> print time
09:45:23

用__new__来实现单例(singleton):

class singleton:
    def __new__(cls):
        if not hasattr(cls,'instance'):
            cls.instance=object.__new__(cls)
        return cls.instance
s1=singleton()
s2=singleton()
print(id(s1))
print(id(s2))
输出:
1555195883928
1555195883928
附:关于 __init__(self) 和 __new__(cls) 的最佳讨论文章(http://www.cnblogs.com/ifantastic/p/3175735.html)

内容摘录:

在 Python 中存在于类里面的构造方法 __init__() 负责将类的实例化,而在 __init__() 启动之前,__new__() 决定是否要使用该 __init__() 方法,因为__new__() 可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例。

如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参数则是生产所需原料,__init__()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而__new__()则是生产部经理,__new__()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。

关于 self 参数

self代表类的实例,而非类

self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。

class Test:
    def printSelf(self):
        print(self)
        print(self.__class__)

x=Test()
x.printSelf()
输出:
<__main__.Test object at 0x00000206F7E2F278>
<class '__main__.Test'>

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self

三、类对象的生命周期

    1. 定义类。

    2. 使用类创建对象。

        需要调用类的构造方法 __init__(self)。创建新对象时系统会自动调用构造方法,传入新创建的对象,可以为新对象的属性赋初始值。

    3. 访问对象成员,包括属性和方法。

    4. 销毁对象时调用析构方法 __del__()。传入要销毁的对象,回收对象所占用的资源。

在类中定义方法时,self 是方法的默认参数,用于指代类实例对象本身。当需要访问类实例对象的属性时,需要使用“self.属性”访问。调用类实例对象的方法时,需要使用“self.方法(参数)”访问。

Python 允许在任何时候添加、删除或修改类和对象的属性。使用赋值运算符可以添加、修改属性值。使用 del 语句可以删除出类对象的属性。

语法格式:del 类名.属性名

  四、类的属性

    类由属性和方法组成。属性分为类属性和实例属性。

    类属性与类绑定,不依赖于实例对象,又称为静态属性。不需要实例化对象,类和对象都可以访问获取类属性的值。

class Plane:
    pCount=0
    def __init__(self):
        Plane.pCount+=1 #初始化实例时,类属性加1

print('已生成 {} 架飞机。'.format(Plane.pCount))
p1=Plane()
print('p1(通过类属性访问)> 已生产 {} 架飞机。'.format(Plane.pCount))
print('p1(通过实例属性访问)> 已生产 {} 架飞机。'.format(p1.pCount))
p2=Plane()
p2.pCount = 0    #为对象p2创建一个与类属性同名的实例属性pCount=0
print('p2(通过类属性访问)> 已生产 {} 架飞机。'.format(Plane.pCount))
print('p2(通过实例属性访问)> 已生产 {} 架飞机。'.format(p2.pCount))
print('p2(通过p1实例属性访问)> 已生产 {} 架飞机。'.format(p1.pCount))
输出:
已生成 0 架飞机。
p1(通过类属性访问)> 已生产 1 架飞机。
p1(通过实例属性访问)> 已生产 1 架飞机。
p2(通过类属性访问)> 已生产 2 架飞机。    #类属性pCount=2,未更改
p2(通过实例属性访问)> 已生产 0 架飞机。    # p2.pCount = 0 p2 的同名实例属性pCount设置为0
p2(通过p1实例属性访问)> 已生产 2 架飞机。

实例属性存在于实例对象中,必须先创建实例对象,才能访问获取其值。且每一个实例对象都有属于自己的实例属性值,例如,self.name 和 self.age 等。

当通过实例对象访问属性时,解释器会先尝试在实例命名空间中寻找,如果找不到,就会去类属性中去查找。只有当对象属性中不存在与类属性同名的实例属性的情况下,才可以通过实例对象访问类属性。

注意:使用实例对象只能读取而不能修改类属性的值。当试图通过实例对象给类属性赋值时,解释器会在实例对象中创建一个与类属性同名的实例属性。例如,p2.pCount=0。

实例属性可以在构造函数中声明并初始化,也可以通过赋值语句声明并赋值。

实例属性只能通过实例对象访问,不能通过类访问实例属性。

五、类的方法

在类地内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例。

类中定义的方法有三种,实例方法、类方法和静态方法

1. 使用隐含参数 self 定义的方法为实例方法 ,与类绑定且依赖于对象。在类中声明定义,必须先创建实例对象,再调用执行。类对象可以调用执行。

2.  类方法使用隐含参数 cls 定义,使用装饰器@classmethod。类方法与类绑定,不依赖于对象,不需要实例化对象,类和类的实例对象都可以调用执行。

    @classmethod
    def cmethod(cls):
        cls.sum+=1
        print('cmethod...',cls.sum)

3. 静态方法与类方法类似,但使用的是 @staticmethod 装饰器,且没有隐含的 cls 参数。

六、类的封装

    1. 封装是指将某些东西包装和隐藏起来,让外界无法直接使用,只能通过某些特定的方式才能访问。被封装的特性只能通过特定的行为去访问,以保证对象的数据完整性、隐藏性和安全性,并且有利于后期的维护。

    2. 在 Python 解释器中,名称前以单下划线“_”为前缀的属性和方法被解释为非公开(保护的)属性和方法。名称前以双下划线“__”作为对象属性名称前缀的属性称为私有属性,外界无法直接查看和修改该属性,需要通过方法对属性进行修改。

以双下划线开头和结尾的(例如,__init__)表示 Python 中的一些特殊方法名。

关于下划线的解读文章:

http://python.jobbole.com/81129/

http://blog.163.com/jackylau_v/blog/static/175754040201182113817834/

class Student:
    def set_name(self,name):
        self.__name=name
    def get_name(self):
        return self.__name
    def set_sex(self,sex):
        self.__sex=sex
    def get_sex(self):
        return self.__sex
    def set_age(self,age):
        self.__age=age
    def get_age(self):
        return self.__age

一个属性写两个方法:set_XXX(参数) 赋值;get_XXX() 取值。