私有化
如果有一个对象,当需要对其进行修改属性时,有二种方法:
- 对象名.属性名 = 数据 ------>直接修改;
- 对象名.方法名()------->间接修改;
为了更好的保护属性安全,既不能随意修改,一般的处理方式为:
- 将属性定义为私有属性;
- 添加一个可以调用的方法,供调用;
- 私有变量在外界是不会被修改的
- 如果想修改,就需要定义set函数
- get函数是用来获取私有变量的
class Person:
def __init__(self,name,age):
self.__name = name
self.__age = age
self.__score = 59
def setAge(self,age):
if age > 0 and age <=120:
self.__age=age
else:
print('年龄不在规定范围内')
def getAge(self):
return self.__age
def __str__(self):
return '姓名是:{},年龄是:{}'.format(self.__name,self.__age)
p = Person('小明',18)
print(p)
p.name='小强' #外界改变不生效
print(p)
- 定义一个私有变量,
p = Person('xiaoming',18)
print(p)
p.setAge(20) #通过set函数改变
print(p)
print('p的你年龄是:',p.getAge()) #获取私有变量
print(dir(Person)) #可以看到类中自带的魔术方法
#print(p.__dir__())
print(dir(p))
- 在类中定义的方法,变量统称为attribute,可以用dir方法进行查看
- python解释器在底层对私有变量进行了重命名,将__age重命名为_person__age
使用装饰器
- 需要添加装饰器
- 可以使私有变量被调用的形式和和普通变量一样
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age
@property #相当于get函数,要先定义
def age(self):
return self.__age
@age.setter #相当与set函数,要和上面的定义同步
def age(self,age):
if age > 0 and age <=120:
self.__age=age
else:
print('年龄不在规定范围内')
def __str__(self):
return '姓名是:{},年龄是:{}'.format(self.name,self.__age)
p = Person('小明',18)
print(p.age)
p.age=120
print(p.age)
关联关系
import random
class Road:
def __init__(self,name,len):
self.name = name
self.len = len
class Car:
def __init__(self,brand,speed):
self.brand = brand
self.speed = speed
def get_time(self,road):
ran_time = random.randint(1,10)
msg = '{}品牌的车在以{}速度行驶{}小时'.format(self.brand,road.name,self.speed,ran_time)
print(msg)
def __str__(self):
return '{}品牌的,速度:{}'.format(self.brand,self.speed)
r = Road('京藏高速',12000)
r.name = '京哈高速'
print(r.name)
audi = Car('奥迪',120)
print(audi)
audi.get_time(r)
class Computer:
def __init__(self,brand,type,color):
self.brand = brand
self.type = type
self.color = color
def online(self):
print('正在用电脑上网')
def __str__(self):
return self.brand + '---' + self.type + '---' + self.color
class Book:
def __init__(self,bname,author,number):
self.bname = bname
self.author = author
self.number = number
def __str__(self):
return self.bname +'---'+ self.author+'---'+str(self.number)
class Student:
def __init__(self,name,computer,book):
self.name = name
self.computer = computer
self.books = []
self.books.append(book)
def borrow_book(self,book):
for book1 in self.books:
if book1.bname == book.bname:
print('已借过此书')
break
else:
self.books.append(book)
print('添加成功')
def show_book(self):
for book in self.books:
print(book.bname)
def __str__(self):
return self.name + '---' + str(self.computer)+'---'+str(self.books)
computer = Computer('mac','mac pro 2020','灰色')
book = Book('盗墓笔记','南派三叔',10)
stu = Student('xiaoming',computer,book)
print(stu)
book1 =Book('斗破苍穹','天蚕土豆',8)
stu.borrow_book(book1)
stu.show_book()
封装
- 封装是面向对象编程的一大特点
- 面向对象编程的第一步 将属性和方法封装到一个抽象的类中
- 外界使用类创建对象,然后让对象调用方法
- 对象方法的细节都被封装在类的内部
-
将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。
-
“封装”就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类);封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。
-
而这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法。那如何定义类的方法呢?
class Person:
def __init__(self, name, weight):
self.name = name
self.weight = weight
def __str__(self):
string = '我的名字是{},体重为{}'.format(self.name, self.weight)
return string
def run(self):
print('%s去跑步。。。' % self.name)
self.weight -= 0.5
def eat(self):
print('%s 去吃东西。。。。。' % self.name)
self.weight += 1
p = Person('小明', 75)
p.run()
p.eat()
print(p)
案例解析
- 房子有户型,总面积和家具名称列表
新房子是没有家具的- 家具有名字和占地面积,其中
eg:占地 6平方米- 将以上三件家具添加到房子中
- 打印房子的时候,要求输出:户型 总面积 剩余面积 家具名称列表
class HouseItem:
def __init__(self, name, area):
self.name = name
self.area = area
def __str__(self):
return '[%s] 占地 %.2f' % (self.name, self.area)
class House:
def __init__(self, house_type, area):
self.house_type = house_type
self.area = area
self.free_area = area
self.item_list = []
def __str__(self):
return '户型:%s \n总面积:%.2f [剩余:%.2f] \n家具:%s' % (self.house_type, self.area, self.free_area, self.item_list)
def add_item(self, item):
if item.area > self.free_area:
print('%s 的面积太大,无法添加' % item.name)
return
self.item_list.append(item.name)
self.free_area -= item.area
bed = HouseItem('床', 400)
chest = HouseItem('柜子', 2)
table = HouseItem('桌子', 4.5)
my_home = House('两室一厅', 200)
print(bed, chest, table, my_home)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
- 士兵瑞恩有一把AK47
- 士兵可以开火(士兵开火扣动的是扳机)
- 枪 能够 发射子弹(把子弹发射出去)
- 枪 能够 装填子弹 --增加子弹的数量
class Gun:
def __init__(self, model):
self.model = model
self.bullet_count = 0
def add_bllet(self, count):
self.bullet_count += count
def shoot(self):
if self.bullet_count <= 0:
print('%s 没有子弹了。。。。' % self.model)
return
self.bullet_count -= 1
print('%s--------%s' % (self.model, self.bullet_count))
class Soldier:
def __init__(self, name):
self.name = name
self.gun = None
def fire(self):
if self.gun == None:
print('%s 没有枪。。。' % self.name)
return
self.gun.add_bllet(50)
self.gun.shoot()
ak = Gun('ak47')
ryan = Soldier('Ryan')
ryan.gun = ak
ryan.fire()
ryan.fire()
继承
-
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
-
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”,继承的过程,就是从一般到特殊的过程。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
-
继承概念的实现方式主要有2类:实现继承、接口继承。
-
实现继承是指使用基类的属性和方法而无需额外编码的能力。
-
接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。
继承类的构造方法:
经典类的写法: 父类名称.init(self,参数1,参数2,…)
新式类的写法:super(子类,self).init(参数1,参数2,…)
深度优先和广度优先
python3一般是广度优先
经典类:
class P1:
def foo(self):
print 'p1-foo'
class P2 :
def foo(self):
print 'p2-foo'
def bar(self):
print 'p2-bar'
class C1 (P1,P2):
pass
class C2 (P1,P2):
def bar(self):
print 'C2-bar'
class D(C1,C2):
pass
d = D()
d.foo() # 输出 p1-foo
d.bar() # 输出 p2-bar
- 实例d调用foo()时,搜索顺序是 D => C1 => P1
- 实例d调用bar()时,搜索顺序是 D => C1 => P1 => P2
- 换句话说,经典类的搜索方式是按照“从左至右,深度优先”的方式去查找属性。d先查找自身是否有foo方法,没有则查找最近的父类C1里是否有该方法,如果没有则继续向上查找,直到在P1中找到该方法,查找结束。
- 深度优先算法:深度优先搜索算法(Depth-First-Search),是搜索算法的一种。
- 是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。
- 这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。
- 属于盲目搜索
新式类:
class P1(object):
def foo(self):
print 'p1-foo'
class P2(object):
def foo(self):
print 'p2-foo'
def bar(self):
print 'p2-bar'
class C1 (P1,P2):
pass
class C2 (P1,P2):
def bar(self):
print 'C2-bar'
class D(C1,C2):
pass
d=D()
d.foo() # 输出 p1-foo
d.bar() # 输出 c2-bar
- 实例d调用foo()时,搜索顺序是 D => C1 => C2 => P1
- 实例d调用bar()时,搜索顺序是 D => C1 => C2
- 可以看出,新式类的搜索方式是采用“ 广度优先”的方式去查找属性。
- 广度优先算法:广度优先算法(Breadth-First Search),同广度优先搜索,又称作宽度优先搜索,或横向优先搜索,简称BFS,是一种图形搜索演算法。
- 简单的说,BFS(也是一种盲目搜寻法)是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,则演算终止。
- 广度优先搜索的实现一般采用open-closed表。
- 目的是系统地展开并检查图中的所有节点,以找寻结果。
- 换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
无参数继承
class Person:
def __init__(self):
self.name='小明'
self.age=18
def eat(self):
print(self.name+'正在吃饭')
def run(self):
print(self.name+'正在跑步')
class Student(Person):
def __init__(self):
print('----->student的init')
super().__init__()
s=Student()
s.run()
有参数继承
class Person:
def __init__(self,name):
self.name=name
self.age=18
def eat(self):
print(self.name+'正在吃饭')
def run(self):
print(self.name+'正在跑步')
class Student(Person):
def __init__(self,name):
print('----->student的init')
super().__init__(name)
s=Student('jack')
s.run()
每个子类有独特的属性
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(self.name + '正在吃饭')
def run(self):
print(self.name + '正在跑步')
class Student(Person):
def __init__(self, name, age, class1):
super().__init__(name, age) #1
self.class1 = class1
def study(self, course): #2
print('{}正在学习{}课程'.format(self.name, course))
def eat(self, food):
super().eat() #3
print('{}喜欢吃{}'.format(self.name, food))
def __str__(self):
return '学生的姓名:{} 年龄:{} 班级:{}'.format(self.name, self.age, self.class1)
class Employee(Person):
def __init__(self, name, age, salary, manager):
super().__init__(name, age)
self.salary = salary
self.manager = manager
def __str__(self):
return '雇员的姓名:{} 年龄:{} 薪资:{} 老板:{}'.format(self.name, self.age, self.salary, self.manager)
s = Student('jack', 12, 'python1')
e = Employee('tom', 22, 10000, 'king')
print(s)
print(e)
s.run()
s.study('python基础')
s.eat('满汉全席')
- 子类可以通过super函数继承父类的属性,并且可以定义自己的属性
- 子类可以定义自己的方法
- 子类可以使用父类的方法,也可以重写父类的方法,注意:改重写的方法名必须和父类方法同名
案例解析
编写一个简单的工资管理程序,系统可以管理以下四类人:工人(worker)、销售员(salesman)、经理(manager)、
销售经理(salemange)。所有的员工都具有员工号,姓名,工资等属性,有设置姓名,获取姓名,获取员工号,计算工资等方法。
- 工人:工人具有工作小时数和时薪的属性,工资计算法方法为工作小时数*时薪。
- 销售员;具有销售额和提成比例的属性,工资计算方法为销售额+提成比例。
- 经理:具有固定月薪的属性,工资计算方法为固定月薪。
- 销售经理:工资计算方法为销售额*提成比例+固定月薪。
请根据以上要求设计合理的类,完成以下功能:
- 添加所有类型的人员
- 计算月薪
- 显示所有人工资情况
class Person:
def __init__(self, no, name, salary):
self.no = no
self.name = name
self.salary = salary
def __str__(self):
msg = '工号:{} 姓名:{} 本月工资:{}'.format(self.no, self.name, self.salary)
return msg
def getSalary(self):
return self.salary
class Worker(Person):
def __init__(self, no, name, salary, hours, per_hour):
super().__init__(no, name, salary)
self.hours = hours
self.per_hours = per_hour
def getSalary(self):
money = self.hours * self.per_hours
self.salary += money
return self.salary
class Salesman(Person):
def __init__(self, no, name, salary, salemoney, percent):
super().__init__(no, name, salary)
self.salemoney = salemoney
self.percent = percent
def getSalary(self):
money = self.salemoney * self.percent
self.salary += money
return self.salary
w=Worker('001','king',2000,160,50)
s=w.getSalary()
print('月薪:',s)
print(w)
print('-------------------------------')
saler = Salesman('002','lucy',5000,5000000,0.003)
s1=saler.getSalary()
print('月薪:',s1)
print(saler)
多重继承
class A:
def test(self):
print('------>AAAAA')
class B:
def test1(self):
print('------>BBBBB')
class C(A,B):
def test2(self):
print('------>CCCCC')
c = C()
c.test()
c.test1()
c.test2()
查看继承顺序
print(inspect.getmro(C))
print(C.__mro__)
多态
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
- 多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
- 在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。
- 也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
多态性的好处
- 增加了程序的灵活性,以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
- 增加了程序额可扩展性,通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
class Person:
def __init__(self, name):
self.name = name
def feed_pet(self, pet):
if isinstance(pet, Pet): #判断这个object是不是该类子类的对象
print('{}喜欢养宠物{},昵称是{}'.format(self.name, pet.role, pet.nickname))
else:
print('不是宠物')
class Pet:
role = 'Pet'
def __init__(self, nickname, age):
self.nickname = nickname
self.age = age
def show(self):
print('昵称:{},年龄:{}'.format(self.nickname, self.age))
class Cat(Pet):
role = '猫'
def catch_mouse(self):
print('抓老鼠')
class Dog(Pet):
role = '狗'
def watch_house(self):
print('看房子')
class Tiger:
def eat(self):
print('危险')
cat = Cat('花花', 2)
dog = Dog('旺旺', 4)
tiger = Tiger()
p = Person('小明')
p.feed_pet(cat)
p1 = Person('小强')
p1.feed_pet(tiger)
单例模式
设计模式
- 设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都
- 是针对某一特定问题的成熟解决方案
- 使用设计模式是为了可重用代码,让代码更容易被他人理解,
- 保证代码可靠性
单例设计模式
- 目的:让类创建对象,在系统中只有唯一的一个实例(对象)
- 每一次执行类名()返回的对象 内存地址是相同的
我们用 类名. 的方式创建对象的时候,python解释器会帮我们做两件事情
- 为对象分配空间
- 对象初始化
使用类名()创建对象时,python的解释器首先会调用__new__方法为对象分配空间__new__是一个由object基类提供的内置的静态方法,主要有两个作用:
- 在内存中为对象分配空间
- 返回对象的引用
- python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
- new:负责给对象分配空间 init(初始化方法)负责给对象初始化
- 我们要重写new方法,使用类名()创建对象的时候,返回的都是同一个地址
重写__new__方法的代码非常固定:
- 继承自父类方法
- 返回父类方法调用_new__方法的结果
- 重写__new__方法一定要return object.new(cls),否则python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
class Singleton:
__instance = None
def __new__(cls, *args, **kwargs):
print('------>new')
if cls.__instance is None:
print('---->1')
cls.__instance = object.__new__(cls)
print(cls.__instance)
return cls.__instance
else:
print('------>2')
return cls.__instance
s = Singleton()
s1 = Singleton()
print(s)
print(s1)
可见,两个实例申请的内存地址是一样的,即new方法返回的地址