文章目录
面向对象实例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正在面试新员工