python笔记之第八天--面向对象编程OOP

面向对象实例1

class Book:#类Book相当于一种数据类型
    def  __init__(self,title,price=0.0,author=None):#可以写默认值,
        # 此时即对于默认参数实例化时可以赋值也可以不赋值了,
        # 但默认参数必须在命名参数后边,即必须在title后边
        #0.0可简写为.0
        #__init__为预定义函数,一会实例化时,会自动执行这个构造函数,将你给的实参传过来即进行属性赋值。
        self.title = title
        self.price = price
        self.author = author
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    print(book1.price)
    book1.print_info()

在交互式控制台中运行显示

注: 1 .py文件命名可以使用数字,字母,下划线,当文件作为主程序运行时,文件名开头可以随意,以数字,字母,下划线开始都可以,如1a.py,aa.py,_a1.py,a1.py,_1a.py;

2 但当.py文件作为模块被导入到其他文件中调用,则作为模块的.py文件不能以数字开头命名,可以用下划线和字母开头,如aa.py,_a1.py,a1.py,_1a.py,若命名为1a.py则出错,对主程序命名则无要求。

在这里插入图片描述

import  e08
b=e08.Book('python')
b.price
0.0
b
<e08.Book object at 0x0000022658BB1588>#实例b为类Book的一个对象且在内存为,这对用户没用,至少应该显示标题,定价等有用信息。
 在控制台中不希望看到输入实例b,出来这个结果<e08.Book object at 0x0000022658BB1588>,
 所以可在类中加__repl__(self)函数。 如下图:

在这里插入图片描述

测试结果:
在这里插入图片描述
注:__repr__实际中在控制台测试时,可以显示实例本身如b的信息。主要用在调试用。
此时,在控制台上print(实例对象b)时,也会打印__repr__的呈现形式。原因在于你在类中没有写def __str__(self):这个函数。此时就用__repr__(self)代替去呈现了,若你写了def str(self):`这个函数,此时print(实例b)时,在控制台就会呈现自己str函数所定义的形式了。
如下图表示:
在这里插入图片描述
因此_repr__实际中在控制台测试时,想简要看一下这个对象,主要面向程序员。而 str(self):函数主要面向用户,让普通用户在控制台可以看的明白一些。

self释义

翻译为:自己本身意思。如下图:
在这里插入图片描述

定义全局属于类的属性,即不依附于某一个实例的属性。

例如:count统计图书数量

class Book:#类Book相当于一种数据类型
    count=0#与类有关,但与实例无关的东西,放在类中,且不要加self,如count
    #来统计数量,即此时每建一个实例书,即让它加1.
    def  __init__(self,title,price=0.0,author=None):#可以写默认值,
        self.title = title
        self.price = price
        self.author = author
        #self.count=count#这样写因为有self表示这个东西与实例有关,即依附于实例存在
    def  __repr__(self):
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)

    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    Book.count+=1#类属性调用方式
    book2 = Book('pytho', '49', 'tom')
    Book.count += 1  # 类属性调用方式,这样不管什么时候都是通过类去取count的
    print('图书数量{}'.format(Book.count))
    book1.print_info()

测试结果:

图书数量2
python经典 49 tom

总结:在定义类成员时要规划清楚,这个成员与类对象有关,还是与实例(要加self)关系更紧密。因此对于类全局的成员,与实例无关,可不加self,写在类中;调用时通过类名方式调用。类似其他其他编程语言的静态成员。总之,不要加self就为依附于类对象存在了。

不用每次创建时,都调用count+1,通过每定义一个实例,都会执行构造函数,来实现自动加1。

class Book:#类Book相当于一种数据类型
    count=0#与类有关,但与实例无关的东西,放在类中,且不要加self,如count
    def  __init__(self,title,price=0.0,author=None):
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1#这样的话由于每创建一个实例会自动执行__init__,所以写在这,可以实现自动加+1
    def  __repr__(self):
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    print('图书数量{}'.format(Book.count))
    book1.print_info()

测试结果:

图书数量2
python经典 49 tom

销毁内存中的一个实例对象

问题?

class Book:#类Book相当于一种数据类型
    count=0
    def  __init__(self,title,price=0.0,author=None):
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1
    def  __repr__(self):
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    del(book1)#此时删除了实例book1,但发现图书数量仍为2
    print('图书数量{}'.format(Book.count))

解决方法:可以用析构函数,即销毁一个对象时,自动执行析构函数,从而实现实例销毁时,图书数量自动减1
尽管del属于全局函数,但我们可以让它在执行时候,在类中自动找到并触发析构函数执行。

class Book:#类Book相当于一种数据类型
    count=0
    def  __init__(self,title,price=0.0,author=None):#初始化对象时用
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1
    def __del__(self):#删除对象时自动执行。
          Book.count -=1 #与实例对象无关,不用加self,且不要返回return
    def  __repr__(self):##这种双下划线的函数具有特殊功能的函数
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    del(book1)#此时删除了实例book1,但发现图书数量仍为2
    print('图书数量{}'.format(Book.count))

注:发现仍可以用实例book1调用在python中定义一个属于类成员,但不依附于实例成员,此时,既可以用类名调用, 也可以用实例调用,但这样就比较乱了,所以尽量避免。

class Book:#类Book相当于一种数据类型
    count=0
    def  __init__(self,title,price=0.0,author=None):#初始化对象时用
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1
    def __del__(self):#删除对象时自动执行。
          Book.count -=1 #与实例对象无关,不用加self,且不要返回return
    def  __repr__(self):##这种双下划线的函数具有特殊功能的函数
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    print('图书数量{}'.format(book1.count))#发现仍可以用实例book1调用
    #在python中定义一个属于类成员,但不依附于实例成员,此时,既可以用类名调用,
    # 也可以用实例调用,但这样就比较乱了,所以尽量避免。
class Book:#类Book相当于一种数据类型
    count=0
    def  __init__(self,title,price=0.0,author=None):#初始化对象时用
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1
    def __del__(self):#删除对象时自动执行。
          Book.count -=1 #与实例对象无关,不用加self,且不要返回return
    def  __repr__(self):##这种双下划线的函数具有特殊功能的函数
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    #当你通过实例book1乱改count时,只是他变了,即属于book1的count了,与我类无关了。但发现类名.count仍不变。
    print('图书数量{}'.format(book1.count))
    print('图书数量{}'.format(Book.count))
    book1.count=99#乱改值。
    print('图书数量{}'.format(book1.count))
    print('图书数量{}'.format(Book.count))

测试结果:

图书数量2
图书数量2
图书数量99
图书数量2

写个函数与实例关系不密切,但与类有关的函数。

一般在Python中像这样def del(self):有self都是与实例有关的,若不想这样而与类有关时,可以这样。

方法1python2延续下来的

这种方法称为未与实例绑定的方法,类方法。

class Book:#类Book相当于一种数据类型
    count=0
    def  __init__(self,title,price=0.0,author=None):#初始化对象时用
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1

    def  cls_method(cls):#cls表示的是类方法,指类的关系而与实例无关,
        print( '类函数')



    def  __repr__(self):##这种双下划线的函数具有特殊功能的函数
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    Book.cls_method(book1)# 但cls实际只是一个占位符,实际用的时候,
    # 它虽然与实例无关,但仍要传个实例过来,不可以删除实例,即必须要有实例才可以运行。
    #所以虽然这个方法确实属于类,但还可以获取实例信息,即为获取实例的某些信息留下了个接口。

方法2python3

class Book:#类Book相当于一种数据类型
    count=0
    def  __init__(self,title,price=0.0,author=None):#初始化对象时用
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1

    
    def  static_method():
        print( '真正静态函数,逻辑上与实例无关')


    def  __repr__(self):##这种双下划线的函数具有特殊功能的函数
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    Book.static_method()

 def  static_method():#但是这样写,又不可以通过实例去调用了book2.static_method()这种不行
        print( '真正静态函数,逻辑上与实例无关')

若既想通过类调用,也想通过实例去调用。

可以加个装饰器。 @staticmethod这是固定的方法,就这样写的

class Book:#类Book相当于一种数据类型
    count=0
    def  __init__(self,title,price=0.0,author=None):#初始化对象时用
        self.title = title
        self.price = price
        self.author = author
        Book.count += 1

    @staticmethod#加上装饰器后,也可以通过实例调用。
    def  method():#但是这样写,又不可以通过实例去调用了book2.static_method()这种不行
        print( '真正静态函数,逻辑上与实例无关')


    def  __repr__(self):##这种双下划线的函数具有特殊功能的函数
        return '<图书为{}>'.format(self.title)
    def __str__(self):
        return  '[图书为{},定价:{}]'.format(self.title,self.price)
    def  print_info(self):
        print(self.title,self.price,self.author)
if __name__=="__main__":
    book1=Book('python经典','49','tom')
    book2 = Book('pytho', '49', 'tom')
    book2.method()#@staticmethod#加上装饰器后,也可以通过实例调用。

实际开发时,有些信息不可以直接写,要做一个过滤。

例如:学生的年龄会随着时间变化更新,但生日不变。所以有时从习惯上说存年龄合适,但逻辑上存生日好,因为年龄会不断更新。

import datetime
class student:
    def __init__(self,name,birthday):
        self.name=name
        self.birthday=birthday

if __name__=="__main__":
    s=student('tom',datetime.date(1992,3,1))
    print(s.birthday)
import datetime
class student:
    def __init__(self,name,birthday):
        self.name=name
        self.birthday=birthday
#从习惯上存年龄好,但逻辑上又不能存年龄,想存年龄,怎么办呢?用一个方法。
    def  get_age(self):#今天的年份-生日年份
     return datetime.date.today() .year-self.birthday.year
     #即写了这个函数值来过滤了值。
if __name__=="__main__":
    s=student('tom',datetime.date(1992,3,1))
    print(s.birthday)
    print(s.get_age())

测试结果:

1992-03-01
27

实际中我们可以写个方法对其过滤,但也可以写个属性的东西。

mport datetime
class student:
    def __init__(self,name,birthday):
        self.name=name
        self.birthday=birthday
#从习惯上存年龄好,但逻辑上又不能存年龄,想存年龄,怎么办呢?用一个方法。
    @property#写这个装饰器,即可将这个方法按照属性用。与方法功能相同,也是来获取年龄的。
    def age(self):
    #通过加上属性装饰器就可以实现关于这个属性的一些操作了,如设置,删除等功能。
        return datetime.date.today().year - self.birthday.year
    @age.setter
    def age(self,values):#名字一样不要紧,因为有装饰器会区分它们的。
            raise AttributeError('禁止别人赋值年龄')
        #即别人若乱改年龄时,相当于填的值就是values
        #对于这个值收不收,对于这个例子中,若收下则起冲突。但有时可以拿给其他成员用。
    @age.deleter#也不允许别人随便删除年龄这个值
    def age(self):
        raise  AttributeError('年龄不可以删除')
if __name__=="__main__":
    s=student('tom',datetime.date(1992,3,1))
    print(s.birthday)
    print(s.age)#此时直接写s.age即按照属性方式来用了。
    #用它与函数本质没区别,但有个好处:加上装饰器后,可以进一步过滤,如
    #可以防止别人去随便改年龄,即不允许别人随便为年龄赋值
    s.birthday=datetime.date(1982,3,1)#属性生日与名字可以修改与删除
    #s.age=20#但是此时年龄不可以随便改与删除了,否则会抛出异常
    del (s.age)
    print(s.birthday)
    print(s.age)

测试结果:

Traceback (most recent call last):
1992-03-01
  File "D:/python_pycharm/01/student.py", line 34, in <module>
27
    del (s.age)
  File "D:/python_pycharm/01/student.py", line 25, in age
    raise  AttributeError('年龄不可以删除')
AttributeError: 年龄不可以删除

注:在交互式提示符测试类时,可以导入相应的类文件模块。
当将方法用 @property装饰器修饰后,可以像调属性那样去用啦,但本质仍然是方法。如下图:
在这里插入图片描述
在这里插入图片描述

面向对象实例2(员工类)

面向对象的特征

继承

多个类之间有共同的重叠部分,将重叠共用部分用代码实现写成一个类,即一定程度上可以实现代码重用,当然每个对象也有它自己独有的一些特质,即可单独再去定义。
以下将类间的关系包含与继承关系给他利用过来。

#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author:lenovo
@file: lenovo.py
@time: 2019/12/24
@desc: 员工类,他中有技术员,人事,保洁员,但有一些如名字,薪水这是共有的,而且也有一些各自的特征。如技术员开发
人事负责招聘
"""
class Employ:#类本身就是一种数据类型,将所有员工的共同,关系密切的部分抽象出来写个员工类。基本信息,作为基类超类。
    def __init__(self,department,name,birthday,salary):
        self.department=department
        self.name=name
        self.birthday=birthday
        self.salary=salary
    def  __repr__(self):
        return '<员工的姓名{}>'.format( self.name)
    def working(self):
        print('员工{}在工作'.format(self.name))

#写派生类,即子类。如程序员子类时,此时就不用把也有基本信息再写一遍了,直接继承即可以。只需写自己独有的。
class Programer(Employ):#继承Employ类后,此时如name,birthday,salary这些基本信息可以不用写了。#但赋值时要写即可。
    def __init__(self,department,name,birthday,salary,specialty,project):#程序员特有的speclaity,project
        super().__init__(department,name,birthday)#super为基类或超类,即基本属性赋值由super初始化做即可。
        #自己只需要写两个属性初始化即可。
        self.specialty=specialty
        self.project = project
    def working(self):#这个方法名在基类中也有,称为重写或称为重载,可以体现多态的特征。
        print('程序员工{}在开发项目{}工作'.format(self.name,self.project))
#在交互式提示符下测试时,还得导入这个模块。

控制台测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
继承测试:

#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author:lenovo
@file: lenovo.py
@time: 2019/12/24
@desc: 员工类,他中有技术员,人事,保洁员,但有一些如名字,薪水这是共有的,而且也有一些各自的特征。如技术员开发
人事负责招聘
"""
import  datetime
class Employ:#类本身就是一种数据类型,将所有员工的共同,关系密切的部分抽象出来写个员工类。基本信息,作为基类超类。
    def __init__(self,department,name,birthday,salary):
        self.department=department
        self.name=name
        self.birthday=birthday
        self.salary=salary
    def  __repr__(self):
        return '<员工的姓名{}>'.format( self.name)
    @property#也可以定义一个装饰器,看员工的年龄。
    def age(self):#实际开发时,确实某个值通过计算得到,但不允许它填,可以定义为属性
        return datetime.date.today().year - self.birthday.year
    #因为不同员工有不同的工资调整
    def give_raise(self,percent,bonus=.0):#0.0可以简写为.0
        self.salary=self.salary *(1+percent+bonus)
    def working(self):
        print('员工{}在工作'.format(self.name))

#写派生类,即子类。如程序员子类时,此时就不用把也有基本信息再写一遍了,直接继承即可以。只需写自己独有的。
class Programer(Employ):#继承Employ类后,此时如name,birthday,salary这些基本信息可以不用写了。#但赋值时要写即可。
    def __init__(self,department,name,birthday,salary,specialty,project):#程序员特有的speclaity,project
        super().__init__(department,name,birthday,salary)#super为基类或超类,即基本属性赋值由super初始化做即可。
        #自己只需要写两个属性初始化即可。
        self.specialty=specialty
        self.project = project
    def working(self):#这个方法名在基类中也有,称为重写或称为重载,可以体现多态的特征。
        print('程序员工{}在开发项目{}工作'.format(self.name,self.project))
#在交互式提示符下测试时,还得导入这个模块。且代码改变时,又得重新载入,所以在这个文件下写。

if  __name__=="__main__":#若脚本正在以这个文件运行的话,就会执行这个之后的代码。
    p=Programer('技术部','tom',datetime.date(1992,1,2),8000,'flask','CSM')
    print(p)#这样会调用__str__,展现,但是没写,#只要继承之后也可以调用基类的方法了。所以会调用__repl__来展现
    p.give_raise(.2,.1)#只要继承之后也可以调用基类的方法了。
    print(p.salary)
    p.working()
    print(p.age)#会自动继承下来了

多继承

#class HR(Employ,Manger):可以这样多继承,实际开发时,看到底需不需要多继承,一般不要用。避免混乱。
# 当出现多继承时,再用super__容易混乱,即不知道继承哪个了,就含糊不清了,即不知道调用谁的构造函数了。
#所以一般我们不用super,而是明确调用谁的构造函数;
class HR(Employ):
    def __init__(self,department,name,birthday,salary,qualification_level=1):
        Employ.__init__(self,department,name,birthday,salary)#即显式的说明调用哪个基类的构造函数,此时必须加上self
        self.qualification_level=qualification_level
     #人事可以有自己的工作,所以可以也可以重载基类的working方法
    def working(self):  # 这个方法名在基类中也有,称为重写或称为重载,可以体现多态的特征。
        print('人事{}正在面试新员工'.format(self.name))

整体代码:

#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author:lenovo
@file: lenovo.py
@time: 2019/12/24
@desc: 员工类,他中有技术员,人事,保洁员,但有一些如名字,薪水这是共有的,而且也有一些各自的特征。如技术员开发
人事负责招聘
"""
import  datetime
class Employ:#类本身就是一种数据类型,将所有员工的共同,关系密切的部分抽象出来写个员工类。基本信息,作为基类超类。
    def __init__(self,department,name,birthday,salary):
        self.department=department
        self.name=name
        self.birthday=birthday
        self.salary=salary
    def  __repr__(self):
        return '<员工的姓名{}>'.format( self.name)
    @property#也可以定义一个装饰器,看员工的年龄。
    def age(self):#实际开发时,确实某个值通过计算得到,但不允许它填,可以定义为属性
        return datetime.date.today().year - self.birthday.year
    #因为不同员工有不同的工资调整
    def give_raise(self,percent,bonus=.0):#0.0可以简写为.0
        self.salary=self.salary *(1+percent+bonus)
    def working(self):
        print('员工{}在工作'.format(self.name))

#写派生类,即子类。如程序员子类时,此时就不用把也有基本信息再写一遍了,直接继承即可以。只需写自己独有的。
class Programer(Employ):#继承Employ类后,此时如name,birthday,salary这些基本信息可以不用写了。#但赋值时要写即可。
    def __init__(self,department,name,birthday,salary,specialty,project):#程序员特有的speclaity,project
        super().__init__(department,name,birthday,salary)#super为基类或超类,即基本属性赋值由super初始化做即可。
        #自己只需要写两个属性初始化即可。
        self.specialty=specialty
        self.project = project
    def working(self):#这个方法名在基类中也有,称为重写或称为重载,可以体现多态的特征。
        print('程序员工{}在开发项目{}工作'.format(self.name,self.project))
#在交互式提示符下测试时,还得导入这个模块。且代码改变时,又得重新载入,所以在这个文件下写。
#在定义一个子类
#class HR(Employ,Manger):可以这样多继承,实际开发时,看到底需不需要多继承,一般不要用。避免混乱。
# 当出现多继承时,再用super__容易混乱,即不知道继承哪个了,就含糊不清了,即不知道调用谁的构造函数了。
#所以一般我们继承时不用super,而是明确调用谁的构造函数;采用下面这种方法。
class HR(Employ):
    def __init__(self,department,name,birthday,salary,qualification_level=1):
        Employ.__init__(self,department,name,birthday,salary)#即显式的说明调用哪个基类的构造函数,此时必须加上self
        self.qualification_level=qualification_level
     #人事可以有自己的工作,所以可以也可以重载基类的working方法
    def working(self):  # 这个方法名在基类中也有,称为重写或称为重载,可以体现多态的特征。
        print('人事{}正在面试新员工'.format(self.name))



if  __name__=="__main__":#若脚本正在以这个文件运行的话,就会执行这个之后的代码。
    p=Programer('技术部','tom',datetime.date(1992,1,2),8000,'flask','CSM')
    print(p)#这样会调用__str__,展现,但是没写,#只要继承之后也可以调用基类的方法了。所以会调用__repl__来展现
    p.give_raise(.2,.1)#只要继承之后也可以调用基类的方法了。
    print(p.salary)
    p.working()
    print(p.age)#会自动继承下来了

    hr= HR('人事部','tom',datetime.date(1994,1,2),6000,qualification_level=3)
    hr.give_raise(.1)#人事也可以继承基类的方法与函数了,即give_raise方法
    print(hr.salary)
    hr.working()

测试结果:

<员工的姓名tom>
10400.0
程序员工tom在开发项目CSM工作
27
6600.000000000001
人事tom正在面试新员工
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值