面向对象的三大特征:封装、继承、多态
封装
封装隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。它不仅是面向对象的,面向过程也有大量的封装。
类就是一种封装,通过类将数据和行为相结合,形成一个有机的整体,即将数据和对数据的操作有机的结合。封装的目的是为了增强安全性和简化编程,使用者不必了解实现细节,只需通过外部接口,以特定的访问权限使用类的成员。
成员私有化是封装的主要手段,所有的成员默认是公有的。在类里所有的属性都是公有的,没有私有的,私有是对类外而言
1.1 属性私有化
如果想实现属性私有化,可以将属性的前面增加两个下划线__,表示该属性只能在当前类的方法中被直接访问,不能通过对象访问,这个变量就是私有变量。
class Dog:
#1. __init__构造方法里的代码在实例化时会自动调用,只能有一个__init__构造方法,如果没有定义的话,系统会自动生成一个无参无代码的构造方法。
#不要在__init__构造方法加print()语句,一实例化就会打印出来
def __init__(self,name,age,gender):
self.name=name
self.__age=age
self._gender=gender
def get_age(self): # 2. 其他非构造方法不要同名,否则后者会覆盖前者
return self.__age
def set_Age(self,age):
self.__age = age
dogA = Dog("haski",13,"male") #实例化会执行__init__方法里的代码
dogA.name # "haski"
dogA.age # 报错
dogA._Dog__age # 结果是13 ,此法能访问私有属性,但不建议。
dogA._gender # "male" 是保护变量,但可以正常读写
dogA.__dict__ # {'name': 'haski', '_Dog__age': 18, '_gender': 'male'} __dict__是系统内置变量,所有属性包括受保护的、私有的、公有的全显示了
dogA.get_age() # 13
dogA.set_age(18) # dogA的_Dog__age变成18
上例__age是私有变量,因此可以访问dogA.name,但不能访问dogA.age,可以通过dogA._Dog__age访问和修改,但不建议。
私有变量、保护变量只是一种语法约定,防的是无意中的无必要的访问和修改,不能防恶意的修改。
面试题:
常⻅的在变量的前后加下划线的问题:
单下划线:_age ----->受保护的但可以访问,当约定俗成,当你看到⼀个下划线开头的成员时不应该使⽤它
双下划线:__age ------>私有的
两边都双下划线:age ------->系统内置变量
通常是定义一个公开的方法get_Age访问私有变量,定义一个公开的方法set_Age修改私有变量(如上面代码),
这种方法比较麻烦,可以使用装饰器中的属性装饰器(此前用过函数装饰器),这是一种便捷语法,方便对私有属性访问与修改,这是方法属性化。
class Dog:
def __init__(self, name, age,gender):
self.name = name
self.__age = age
self._gender = gender
@property #代替原有get_age()
def age(self):
return self.__age
@age.setter #代替原有set_age()
def age(self,age):
self.__age = age
访问和修改时就把age方法当成对象的属性进行操作:
dogA = Dog("haski",3,"male")
print(dogA.age)
doaA.age = 10
@property先定义读age,随后@age.setter写
魔术方法
class Dog:
def __init__(self,name,age): # 魔术方法之构造方法
self.name = name
self.__age = age
self.text()
# 魔术方法之析构方法,销毁对象时,进行一些收尾的工作(释放资源) 对象被销毁之前自动调用
# 没有参数 无返回值
def __del__(self):
print("hi,del")
self.fp.close()
def text(self):
self.fp = open("log1.txt", "w")
print("hellowwwewerrqwwer")
# 魔术方法之str方法 当print或str对象时就会调用,获取返回值并输出,代替打印对象的地址
# 没有参数,必须返回字符串
def __str__(self):
return "它没有什么属性"
dogA = Dog("haski",18)
dogA.fp.write("asfsdfsf")
print(dogA) #没有str魔术方法时会出现对象的地址如:<__main__.Retangle object at 0x000001F1B43E7668>
#使用该魔术方法后则出现"它没有什么属性”
成员方法私有化
在方法名前加__,这个方法只能在类内被调用,不能通过对象调用,这就是成员方法私有化。
class Dog:
def __init__(self,name,age):
self.name = name
self.__age = age
__getAge()
def __getAge(self):
print(self.__age)
dogA = Dog('haski',18)# 18
dogA.__getAge() # 报错
作业(修订版)
# 初级
# 1.刘凯买了了一台玫瑰红phone10,价值8000元,可以打电话、玩游戏
class Person:
def __init__(self, name):
self.name = name
def buy(self, phone):
print("{}买了{}{}手机,价格{}元".format(self.name,phone.color,phone.brand,phone.price))
def call(self, phone):
phone.call()
def game(self, phone):
phone.game()
class Phone:
def __init__(self,color,brand,price):
self.color = color
self.brand = brand
self.price = price
def call(self):
print("Make a call")
def game(self):
print("Play a game")
liukai = Person("刘凯")
iPhone = Phone("玫红色","Iphone10",8000)
liukai.buy(iPhone)
liukai.call(iPhone)
liukai.game(iPhone)
class Calc:
# def __init__(self,x,y): # 也可以将构造方法省略,直接在方法里传参,好处是省略构造方法,不好的地方是使用时
# self.x = x # 要每个方法都传参
# self.y = y
def add(self,x,y):
return x + y
def minus(self,x,y):
return x - y
def multiple(self,x,y):
return x*y
def division(self,x,y):
return round(x/y,2)
calc = Calc()
print(calc.add(5,6),calc.minus(5,6),calc.multiple(5,6),calc.division(5,6))
'''
3.设计两个类:
一个点类,属性包括xy坐标。
一个Rectangle类(矩形),属性有左上角和右下角的坐标,可以计算矩形的面积;可以判断点是否在正方形内
实例化一个点,一个正方形,输出正方形的面积,输出点是否在正方形内
'''
设计一个类使用的参数尽量是定义好的另一个类
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return str(self.x) +","+ str(self.y)
class Retangle:
def __init__(self, left, right):
self.left = left
self.right = right
def area(self):
return (self.right.x - self.left.x) * (self.left.y - self.right.y)
def isIn(self, point):
if (self.right.x >= point.x >= self.left.x) and (self.left.y >= point.y >= self.right.y):
# 下面的打印语句中point对象会显示成内存地址,可以在Point类里增加str魔术方法
print("点({})在正方形里{}、{}里。".format(point,self.left,self.right))
else:
print("点({})不在正方形里{}、{}里。".format(point,self.left,self.right))
point1 = Point(5,6)
point2 = Point(10,3)
point3 = Point(8,8)
r1 = Retangle(point1,point2)
print(r1.area())
r1.isIn(point3)
from random import randint
'''
4.定义一个Time类,成员属性包括:时、分、秒;成员⽅方法:
add_hour(self,num) 把小时加num
add_minute(self,num) 把分钟加num
add_second(self,num) 把秒数加num
重写__str__(self)方法,返回格式化的时间字符串:"04:16:09"
'''
# 审题不对,要考虑进位的因素,而且每个方法都应该是能独立计算时间的
class Time:
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
def add_second(self, num):
res = self.second + num
self.second = res % 60
self.add_minute(res//60) # 这个是重点,传值给类内方法,由该方法完成后一步工作(分钟处理)
# self.minute = res // 60 % 60
# self.hour = res // 3600 % 24
def add_minute(self, num):
res = self.minute + num
self.minute = res % 60
self.add_hour(res//60)
def add_hour(self, num):
res = self.hour + num
self.hour = res % 24
def __str__(self):
return "{:>02}:{:>02}:{:>02}".format(self.hour, self.minute, self.second)
now = Time(23, 59, 59)
# now.add_second(1)
# now.add_minute(2)
now.add_hour(192)
print(now)
'''
中级
1.请写一个小游戏,⼈狗⼤战,2个角色,⼈和狗,游戏开始后,生成2个人,3条狗,互相混战,⼈被狗咬了会掉血,
狗被⼈打了也掉血,狗和⼈的攻击力,具备的功能都不一样。
类:⼈
属性:⼈:攻击⼒(打)、生命值(血)
方法:打
类:狗;
属性:攻击⼒(咬)、⽣命值(血)
⽅:咬
'''
class Man:
def __init__(self, name, attack, blood):
self.name = name
self.attack = attack
self.blood = blood
def hit(self, dog):
dog.blood -= self.attack
if dog.blood <= 0:
print("{}被{}打死".format(dog, self.name))
else:
print("{}打{}".format(self.name, dog))
def __str__(self):
return self.name
class Dog:
def __init__(self, name, attack, blood):
self.name = name
self.attack = attack
self.blood = blood
def bite(self, man):
man.blood -= self.attack
if man.blood <= 0:
print("{}被{}咬死b".format(man, self.name ))
else:
print("{}咬{}".format(self.name,man))
def __str__(self):
return self.name
dogList = [Dog("德牧", 20, 100), Dog("藏獒", 30, 200), Dog("吉娃娃", 5, 200)]
personList = [Man("柯南", 10, 180), Man("徐天", 40, 250)]
while len(dogList) > 0 and len(personList) > 0:
if randint(0,1) == 0: # 狗先发动
dogList[randint(0,len(dogList)-1)].bite(personList[randint(0,len(personList)-1)])
for each in personList:
if each.blood <= 0:
personList.remove(each)
if randint(0,1) == 1: # 人先发动:
if len(personList) == 0:
break
personList[randint(0,len(personList)-1)].hit(dogList[randint(0,len(dogList)-1)])
for each in dogList:
if each.blood <= 0:
dogList.remove(each)
print("*"*50)
for each in dogList:
print(each,each.blood)
print("*"*50)
for each in personList:
print(each,each.blood)