面向对象程序设计方法的基本思想是将任何事物都当做对象,是其所属对象类的一个实例。
定义:类是具有相同属性和方法的对象的集合。类的属性,实际上就是类内部的变量;类的方法是在类内部定义的函数。
对象是具体的事物,是类实例化 的结果。每个对象的属性值可能不同,但所有由同一个类实例化的来的对象拥有共同的属性和方法。
格式:使用类之前,先定义类。类定义后就产生一个名字空间,与函数类似,在类内部使用的属性,相当于函数中的变量名。
class Myclass:
'''
一个简单实例类
'''
i=1235
def f(self):
return('hello word')
#实例化类
x=Myclass()
#访问类的属性和方法
print('myclass类的属性i为:',x.i)
print('myclass类的方法f()输出为',x.f())
#很多类都倾向于将对象创建为有初始状态的,因此类可能会定义一个名为__init__()
#的特殊方法(构造方法,如下)
def __init__(self):
self.data=[]
#类定义了__init__()方法的话,类的实例化操作会自动调用__init__()方法。所以,
#在下例中,可以创建一个这样新的实例
x=Myclass()
#__init__()方法可以有参数,参数通过__init__()传递到类的实例化操作上,如
class Complex:
def __init__(self,realpart,imagpart):
self.r=realpart
self.i=imagpart#对象属性
x=Complex(3.0,-4.5)
print(x.r,x.i)
#self代表类的实例,而非类
# 类的方法和普通函数只有一个区别--必须有一个额外的参数,self
class Test:
def prt(sel):
print(sel)
print(sel.__class__)
t=Test()
t.prt()
#从执行结果可看出,self代表的是类的实例,代表当前对象的地址
#self.class则指向类。self不是关键字,换成其他字符也是可以的
class People2:
#定义基本属性
name=''
age=0
#定义私有属性,私有属性在类外部无法直接进行访问
__weight=0
#定义构造方法
def __new__(cls,*args,**kwargs):
print('执行__new__',args[0],args[1])
return object.__new__(cls)
def __init__(self,n,a,w):
self.name=n
self.age=a
self.__weight=w
def speak(self):
print("%s说他%d岁,%d斤"%(self.name,self.age,self.__weight))
p=People2('小明',20,80)
p.speak()
__new__(cls,*agrs,**kwargs):在类调用init方法时,new方法决定是否要用init方法,因为new可以调用其他类的方法,或者返回别的对象来作为本类的实例;
class Student:
def __new__(cls, *args, **kwargs):
print("执行__new__",args[0],args[1]);
return object.__new__(cls);#固定语法
def __init__(self,name,age):
print("执行__init__",name,age);
self.name=name;
self.age=age;
def showName(self):
print("姓名:{}".format(self.name));
zhangsan=Student("张三",22);
zhangsan.showName();
对象属性:私有 共有
如果有一些属性不想给外部权限来访问或者修改,可以在属性或者方法的名字前加两个下划线 ,声对象属性明为私有属性。则在外部调用该属性时抛出错误.
class Person(object):
def __init__(self, name):
self.name = name;
self._title = 'Mr';
self.__job = 'Student';#"__"声明为私有属性
p = Person('Bob');
print (p.name);
print (p._title);
print (p.__job);
封装:将属性和方法封装,外部是不可见的,只有类提供的接口才能与属于类的实例进行信息交换。
步骤分2步:先将属性私有化,在用get set 方法
更好的解决方法是用@property注解
__del__():自动销毁方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print("对象张三被销毁")
zhangsan = Person("张三", 20)
zhangsan.__del__();
测试一个对象被引用多少次:import sys,位置在第一行
语法:sys.getrefcount(t)返回 从2开始
import sys#此行代码需写在编辑器头部
class Person:
def __init__(self,name,age):
self.name=name;
self.age=age;
zhangsan=Person("张三",20);
print(sys.getrefcount(zhangsan));
- 访问私有属性可以通过 对象._类名__属性访问到,但不建议用
- id()函数,用于的代对象的标识符
- 类属性区别于对象属性
- 类方法上添加@classmethod装饰器时,可以调用类属性,也可以通过对象调用类属性;
- 修改类属性的2种方式:1.类名.类属性=... ; 2.实例对象__class__类属性=..
- 静态方法:前面加@ataticmethod 可以有参数,和类,对象都没有关系,可通过类和对象调用dir() __dir__ 用于展示一个内容(类或对象)的内部数据
print(dir(zhangsan)); print(zhangsan.___dir___())
# 由于Python是动态语言,根据类创建的实例可以任意绑定属性。 # 给实例绑定属性的方法是通过实例变量,或者通过self变量: # class Student(object): # # gender='' #这个是类属性,下面两个是实例属性的定义方法 # def __init__(self,name): # self.name=name #通过self变量 # s=Student('Bob') # print(s) # s.score=90 #通过实例变量 # # class Student(object): # name='Student' # s=Student() #创建实例s # print(s.name) #打印name属性,因为实例并没有name属性,所以会继续查找class的name属性 # print(Student) # # class Student(object): # def __init__(self,name,score): # self.name=name # self.score=score # bart=Student('Bart',20) # print(bart.name) # 数据封装 # 面向对象编程的一个重要特点就是数据封装。在上面的Student类中,每个实例就拥有各自的name和score这些数据。我们可以通过函数来访问这些数据,比如打印一个学生的成绩 # class Student(object): # def __init__(self,name,score): # self.name=name # self.score=score # bart=Student('Bart',20) # print(bart.name) # def print_score(std): # print('%s:%s'%(std.name,std.score)) #但是,既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法: # class Student(object): # # def __init__(self, name, score): # self.name = name # self.score = score # # def print_score(self): # print('%s: %s' % (self.name, self.score)) #封装的类的方法 #要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入: # bart.print_score() # 这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出name和score,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。 # 封装的另一个好处是可以给Student类增加新的方法,比如get_grade: # class Student(object): # def get_grade(self): # if self.score >= 90: # return 'A' # elif self.score >= 60: # return 'B' # else: # return 'C' # 同样的,get_grade方法可以直接在实例变量上调用,不需要知道内部实现细节: # 小结 # 类是创建实例的模板,而实例则是一个一个具体的对象,哥哥实例拥有的数据都相互独立,胡不影响 #方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据 # 通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现方式 #继承和多态 # 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class) class Animal(object): def run(self): print("Animal is running...") # 当我们需要编写Dog和Cat类时,就可以直接从Animal类继承: # class Dog(Animal): # pass # class Cat(Animal): # pass # 对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。 # 继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法: # dog=Dog() # print(dog.run()) # 当然,也可以对子类增加一些方法,比如Dog类: class Dog(Animal): def run(self): print('Dog is running...') def eat(self): print('Eating meat...') dog=Dog() print(dog.run()) print(dog.eat()) # 继承的第二个好处需要我们对代码做一点改进。你看到了,无论是Dog还是Cat,它们run()的时候,显示的都是Animal is running...,符合逻辑的做法是分别显示Dog is running...和Cat is running...,因此,对Dog和Cat类改进如下: class Dog(Animal): def run(self): print('Dog is running...') class Cat(Animal): def run(self): print('Cat is running...') # 当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。 # 这样,我们就获得了继承的另一个好处:多态。 #要理解什么是多态,我们首先要对数据类型再作一点说明。当我们定义一个class的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样: a = list() # a是list类型 b = Animal() # b是Animal类型 c = Dog() # c是Dog类型 # 判断一个变量是否是某个类型可以用isinstance()判断: print(isinstance(a,list)) print(isinstance(b,Animal)) print(isinstance(c,Dog)) print(isinstance(c,Animal)) # 看来a、b、c确实对应着list、Animal、Dog这3种类型。 # 看来c不仅仅是Dog,c还是Animal! # 不过仔细想想,这是有道理的,因为Dog是从Animal继承下来的,当我们创建了一个Dog的实例c时,我们认为c的数据类型是Dog没错,但c同时也是Animal也没错,Dog本来就是Animal的一种! # 所以,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行: # 要理解多态的好处,我们还需要再编写一个函数,这个函数接受一个 Animal类型 的变量: def run_twice(animal): animal.run() animal.run() # 当我们传入Animal的实例时,run_twice()就打印出: run_twice(Animal()) # 当我们传入Dog的实例时,run_twice()就打印出: run_twice(Dog()) # 看上去没啥意思,但是仔细想想,现在,如果我们再定义一个Tortoise类型,也从Animal派生: class Tortoise(Animal): def run(self): print('Torteise is running slowly') run_twice(Tortoise()) # 你会发现,新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。 # 多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作 # 即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思: # 对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定, # 这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则: # 对扩展开放:允许新增Animal子类; # 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。 # 继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树 # 静态语言 vs 动态语言 # 对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。 # 对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了: class Timer(object): def run(self): print('start...') # 这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
dvd管理系统
class dvd: def __init__(self,name,price,state): self.name=name; self.price=price self.state=state def __str__(self): stat='已借出'; if self.state==1: stat='未借出' return '名称:《%s》 单价:%d 状态:%s'%(self.name,self.price,stat) class DVDManager: #存储 多个dvds 每一个元素 都是一个DVD dvds=[] def init(self): self.dvds.append(dvd('光辉岁月',10,0)); self.dvds.append(dvd('绅士', 5, 1)); self.dvds.append(dvd('海阔天空', 11, 1)); def Menu(self): self.init() while(True): print('1.查询所有DVD') print('2.增加DVD') print('3.借出DVD') print('4.归还DVD') print('5.退出') choose=int(input('请选择:')); if choose==1: self.showAllDVD(); elif choose==2: self.insertDVD(); elif choose==3: self.jiechuDVD(); elif choose==4: self.rollBackDVD(); elif choose==5: print('5.谢谢使用') break; def showAllDVD(self): for d in self.dvds: print(str(d)) def jiechuDVD(self): name = input('请输入DVD的名称:') ret = self.checkDVD(name); if ret != None: if ret.state==0: print('dvd 已经借出去了') #return; else: days=int(input('请输入借出天数:')) ret.state = 0; print('借出 %d 天,应还利息%d '%(days,ret.price*days)) else: print('DVD 不存在!') def rollBackDVD(self): name = input('请输入归还DVD的名称:') ret = self.checkDVD(name); if ret == None: print('DVD 不存在!') else: if ret.state == 1: print('dvd 未借出!') else: days= int(input('请输入借出去了多少天?')) y_money=ret.price * days print('借出 %d 天,应还利息%d ' % (days, y_money)) money= int(input()) #带学生完成.. if money<y_money: print('不够!') return; elif money>y_money: print('找零:%d'%(money-y_money)); ret.state=1; print('归还成功!') def insertDVD(self): name = input('请输入DVD的名称:') ret= self.checkDVD(name); if ret != None: print('DVD 已经存在!') else: price=int(input('请输入价格:')) new_dvd=dvd(name,price,1); self.dvds.append(new_dvd) print('存放成功!') def checkDVD(self,name): for d in self.dvds: if d.name==name: return d;#返回当前对象-->内存地址 else: return None; manager= DVDManager(); manager.Menu();