一、定义
1、简介
面向对象编程:OOP 面向对象编程的关注点在于谁来做,相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法 属性、行为 先有类,把具体的东西抽象并归类,然后根据类再生产出一个个对象 类:是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用
- 特征其实就是一个变量,在类里称之为属性;
- 行为其实就是一个函数,在类里称之为方法;
- 类其实就是由属性和方法组成的一个抽象概念
2、面向对象的三个特征
封装、继承、多态
二、面向对象的基本语法
# 有两个对象:
# 小明今年23岁,身高180cm,体重75kg,它能够睡觉
# 小红今年21岁,身高170cm,体重58kg,它能够睡觉
# 创建一个类,并且定义一个类名
class Person(object):
def __init__(self, name, age, height, weight):
self.name = name
self.age = age
self.height = height
self.weight = weight
def sleep(self): # 行为
print('正在睡觉!')
# 使用Person类创建了一个Person对象 p1
p1 = Person('小明', 23, '180cm', '75kg')
p1.sleep()
p2 = Person('小红', 21, '170cm', '58kg')
p2.sleep()
三、对象的属性
- 使用 点 . 语法,可以给一个对象添加属性;
- python 里支持动态属性(尽量避免使用动态属性);
- 对象在创建的时候,可以不设置属性,创建好了以后,再手动的给对象添加属性(动态添加属性);
- __init__方法要求对象创建的时候就必须要有某些属性
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def say_hello(self):
print('大家好,我的名字是{},我{}岁了'.format(self.name, self.age))
# print('大家好,我的名字是{},我{}岁了'.format(self.name, self.money) # 会报错,因为money属性是后面手动动态添加的
# __init__ 方法要求对象创建的时候就必须要有某些属性
s1 = Student('jerry', 18, 100)
print(s1.name)
print(s1.addr) # 会报错,因为没有addr属性,AttributeError: 'Student' object has no attribute 'addr'
# 对象创建好了以后,还可以随时动态的添加属性
s1.money = 1000
print(s1.money)
print(s1.age) # 18
s1.age = 20
print(s1.age) # 20
s1.say_hello() # 会成功打印,如果print中有动态属性,会报错
s2 = Student('chris', 20, 98)
s2.say_hello() # 会成功打印
四、self的指向
- 会调用 Dog 类的 __new__方法,申请一段内存空间
- 把 __init__里的 self 指向这一段内存空间
- 调用 __init__方法,在内存里放入自己的数据
- 把这段内存空间赋值给变量 d1,即最后d1也会指向这一段内存空间
- self只能在__init__中使用
class Dog(object):
def __init__(self, name, color):
self.xingming = name
self.yanse = color
def say_hello(self): # 谁调用这个方法,self指的就是谁
print('大家好,我是{}'.format(self.xingming))
d1 = Dog('大黄', '白色')
print(d1.yanse)
d1.say_hello() # d1调用了say_hell0()方法,self指的就是d1,self.xingming指的就是d1的姓名'大黄’
d2 = Dog('旺财', '黑色')
d2.say_hello()
五、魔法方法
在类里,以__开始,并且以__结束的方法,我们称之为魔法(魔术方法) 特点: 不用手动的调用,它们会在适当的时机自动调用
- __str__方法必须要有返回值,而且是一个字符串;当一个对象要变成字符串的时候会自动调用;
- __repr __当需要修改一个对象的输出结果时候
- __del__当一个对象被销毁的时候,会自动调用这个方法
class Person(object):
def __init__(self, name, age):
print('__init__函数被调用了!!!!')
self.name = name
self.age = age
def __str__(self):
# 当一个对象要变成字符串的时候会自动调用
# __str__ 方法必须要有返回值,而且是一个字符串
print('__str__被调用了!!!!!')
# return 'hehe'
return 'name:' + self.name + ', age:{}'.format(self.age)
def __repr__(self):
# 当需要修改一个对象的输出结果时候
return 'name:' + self.name + ', age:{}'.format(self.age)
return 'yes'
def __del__(self):
# 当一个对象被销毁的时候,会自动调用这个方法
print('hehehehe')
del self
def sleep(self):
print('{}正在睡觉'.format(self.name))
p1 = Person('jerry', 18) # 自动被调用,会打印'__init__函数被调用了!!!!'
# print(p1) # <__main__.Person object at 0x0069F7D0>
print(p1) # {name:jerry,age:18}
六、==
1、==
- 两个对象做 == 比较,会调用对象的 __eq__方法;
- 如果不修改__eq__方法,两个对象 == 默认也是比较内存地址
- 列表==比较的是内容,is比较的是内存地址
- 对象==若不修改eq方法,默认比较的也是内存地址
# 1、
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person('zhangsan', 18)
p2 = Person('zhangsan', 18)
print(p1 == p2) # False,对象==若不修改eq方法,默认比较的是内存地址
print(p1 is p2) # False
n1 = ['zhangsan', 'lisi', 'wangwu']
n2 = ['zhangsan', 'lisi', 'wangwu']
print(n1 == n2) # True,列表==比较的是内容,is比较的是内存地址
print(n1 is n2) # False
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
# if self.name == other.name and self.age == other.age:
# return True # 如果name和age都一样的话,就返回true,否则返回false
# return False
return self.name == other.name and self.age == other.age # 等价
p1 = Person('张三', 18)
p2 = Person('张三', 18)
p3 = p2 # python的赋值是内存地址的赋值,即把p2的地址给了p3,指向同一块内存地址,p3没有创建新的对象,创建对象的格式如p1,p2
print(p3 is p2) # True
print(p3 is p1) # False
print(p1 == p2) # True,修改了eq方法,如果name和age都一样的话,就返回true
# p1 == p2 ==> p1.__eq__(p2)即p1==p2即p1调用的eq方法
print(p1 is p2) # False
2、比较两个对象的大小
- 默认不支持除了 == 以外的比较运算,只有重写了 __lt__或和 __gt__魔法方法以后,才支持比较运算
- lt: less than 小于 gt: greater than 大于
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __lt__(self, other): # 重写lt方法
return self.age < other.age
p1 = Person('chris', 18)
p2 = Person('allen', 19)
print(p1 > p2) # False
3、给对象排序
- 使用sort方法,参数key传入匿名函数可以对列表里的对象元素进行排序
class Person(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def __lt__(self, other): # 重写it方法
# return self.age < other.age
return self.score < other.score
p1 = Person('jerry', 18, 98)
p2 = Person('tony', 20, 81)
p3 = Person('henry', 17, 86)
persons = [p1, p2, p3]
persons.sort()
# persons.sort(key=lambda ele: ele.score),在没有it方法时(无重写的那段代码)可以把key的📄传进来
for p in persons:
print(p.name)
# 字典排序
ps = [
{'name': 'zhangsan', 'age': 18},
{'name': 'lisi', 'age': 19},
{'name': 'wangwu', 'age': 17},
{'name': 'zhaoliu', 'age': 20},
{'name': 'tianqi', 'age': 16},
{'name': 'wangba', 'age': 21}
]
# def demo(ele): # 会不断地把一个个字典传进来,给参数ele
# return ele['age']
# ps.sort(key=demo)
ps.sort(key=lambda ele: ele['age']) # 可以用lambda表达式简写
print(ps)
七、面向对象的练习
1、小明体重
# 1、小明体重 75.0 公斤,小明每次跑步会减肥 0.5 公斤,小明每次吃东西体重增加 1 公斤。
# 在打印小明这个对象时,输出小明的姓名,体重信息
class Person(object):
def __init__(self, name, weight):
self.name = name
self.weight = weight
def run(self):
self.weight -= 0.5
def eat(self):
self.weight += 1
def __int__(self): # 魔法方法
return 100
def __float__(self): # 魔法方法
return 34.5
def __str__(self): # 魔法方法,在需要返回一个字符串时,自动调用该魔法方法
return '姓名:{},体重{}kg'.format(self.name, self.weight)
p1 = Person('小明', 75)
p1.run()
p1.run()
p1.run()
p1.eat()
p1.run()
print(p1)
print(str(p1))
print(int(p1))
print(float(p1))
2、房子户型、家具
# 2、房子(House)有户型、总面积、剩余面积和家具名称列表属性
# 新房子没有任何的家具
# 将家具的名称追加到家具名称列表中
# 判断家具到面积是否超过剩余面积,如果超过,提示不能添加这件家具;
# 家具(HouseItem)有名字和占地面积属性,其中
# 席梦思(bed)占地4平米
# 衣柜(chest)占地2平米
# 餐桌(table)占地1.5平米
# 将以上三件家具添加到房子中
# 打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表
class House(object):
def __init__(self, type, total_area):
self.type = type
self.total_area = total_area
self.free_area = total_area * 0.5
self.item_list = [] # 给一个空列表
def add_item(self, item):
if self.free_area < item.area * 1.5: # 假设家具占地面积乘以1.5为实际占地面积
print('已经放不下了!')
return # 如果放不下后,return后,之后到代码便不会再走了
self.item_list.append(item.name) # 如果还有剩余面积,往里面添加
self.free_area -= item.area * 1.5
def __str__(self):
return '户型{},总面积{},剩余面积{},家具列表{}'.format(self.type, self.total_area, self.free_area, self.item_list)
class HouseItem(object):
def __init__(self, name, area):
self.name = name
self.area = area
h1 = House('一居室', 20)
bed = HouseItem('席梦思', 4)
chest = HouseItem('衣柜', 2)
table = HouseItem('餐桌', 1.5)
h1.add_item(bed)
h1.add_item(chest)
h1.add_item(table)
print(h1)
3、士兵分配枪
# 3、士兵张三有一把M416
# 枪能够装填子弹
# 枪能够发射子弹
# 如果士兵没有枪,无法开火
# 如果枪里没有子弹,需要装填子弹
# 如果有枪又有子弹,则可以开火
class Solider(object):
def __init__(self, name, gun=None): # 缺省参数,后续不传gun也可以
self.name = name
self.gun = gun
def fire(self):
if not self.gun:
print('你还没有枪,不能开火!!!')
return
if self.gun.bullet_count == 0:
print('没有子弹,需要装填子弹')
return
if self.gun.bullet_count >= 3:
self.gun.bullet_count -= 3 # 每次开枪3连发
else: # 如果子弹小于3,则开火后子弹剩余0
self.gun.bullet_count = 0
print('有枪有子弹,开火!!!还剩{}发子弹'.format(self.gun.bullet_count))
class Gun(object):
def __init__(self, type, bullet_count=0): # 子弹给默认值0
self.type = type
self.bullet_count = bullet_count
g1 = Gun('m416', 30)
s1 = Solider('许三多')
# s1 = Solider('许三多', g1) # 实例创建时就已分配给枪
s1.gun = g1 # 如果没有该句,实例都已创建好,但是s1并没有拿到枪,即创建时没有枪,后来给了一把枪
s1.fire()
4、点是否在线上
# 定义一个类 Point,有两个属性 x,y 创建三个Point对象 a,b,c 判断c是否在ab组成的线段上
import math
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
class Line(object): # 隐藏线索,两段线段长度加起来总和相等,即在同一条线段上
def __init__(self, start_point, end_point):
self.start_point = start_point
self.end_point = end_point
def get_length(self, point1=None, point2=None): # 获取线段长度,即两点之间距离
point1 = point1 or self.start_point
point2 = point2 or self.end_point
return math.sqrt((point2.x - point1.x) ** 2 + (point2.y - point1.y) ** 2) # 开方,math模块
def is_inline(self, point): # 判断点是否在线上
return self.get_length() == self.get_length(self.start_point, point) + self.get_length(self.end_point, point)
p1 = Point(3, 3)
p2 = Point(6, 6)
p3 = Point(6, 7) # false该点不在线上
p4 = Point(9, 9) # true该点在线上
l = Line(p1, p2)
print(l.get_length()) # 如果什么参数也不传,则point1取默认值None,为false,则继续取or后面的值self.start_point,or一直取到第一个为true的值
# print(l.get_length(p1, p3)) # p1传给point1,p3传给point2
print(l.is_inline(p4))
5、点是否在矩形内
定义一个类React,有start_x,start_y,width,height的属性,表示矩形的长和宽。判断(x,y)是否在矩形的范围内
# 1、
class React(object):
def __init__(self, start_x, start_y, width, height):
self.start_x = start_x
self.start_y = start_y
self.width = width
self.height = height
def is_inside(self, x, y):
# if self.start_x < x < self.start_x + self.width and self.start_y < y < self.start_y + self.height:
# return True
# return False
return self.start_x < x < self.start_x + self.width and self.start_y < y < self.start_y + self.height
r = React(4, 5, 10, 6)
print(r.is_inside(20, 25)) # False
print(r.is_inside(12, 9)) # True
# 2、再多加一个point类
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
class React(object):
def __init__(self, start_point, width, height):
self.start_point = start_point
self.width = width
self.height = height
def is_inside(self, point):
return self.start_point.x < point.x < self.start_point.x + self.width and self.start_point.y < point.y < self.start_point.y + self.height
sp = Point(10, 18) # 实例化矩形的起始点:start_point(即start_x,start_y)
r = React(sp, 20, 25)
p = Point(22, 28)
print(r.is_inside(p)) # True
|