1. 面向对象的思想:将变量和函数绑定到一起,分类进行封装,每个程序只要负责分配给自己的分类,这样能够更快速的开发程序,减少了重复代码。
2. 面向对象编程的关注点在于“谁来做”:
- 在完成某一个需求前,首先确定职责 —— 要做的事情(方法)
- 根据 职责 确定不同的 对象,在对象内部封装不同的方法(多个)
- 注重 对象和职责,不同的对象承担不同的职责。
3. 类和对象是面向对象编程的两个核心概念。
- 特征其实就是一个变量,在类里我们称之为属性。
- 行为其实就是一个函数,在类里我们称之为方法。
- 类其实就是由 属性 和 方法 组成的一个抽象概念。
- 对象是由类创建出来的一个具体存在,可以直接使用。由哪一个类创建出来的 对象,就拥有在哪一个类中定义的属性和方法。
- 类中定义了什么属性和方法,对象中就有什么属性和方法。
- 不同对象对应的属性值也会不同。
- 通过内存空间变化理解
1.创建实例对象:
2.类方法
3. 私有属性 私有方法 通过内部接口函数方法去调用;因外部无法访问私有属性和私有方法
4. Python 的类里提供的方法--魔法方法
- __init__() 方法:父类object中默认存在__init__方法,在使用类创建一个对象时默认被调用,在内存开辟一块空间存放该类;在开发中,如果希望在创建对象的同时,就设置对象的属性,可以在类中重写
__init__
方法进行改造。 -
调用__new__方法,申请一段内存空间;
调用__init__方法,并让self指向申请好的那段内存空间
将实例对象的属性参数传入到那段内存空间
即使是创建两个相同名字的对象,但开辟的内存空间是不一样的
-
__init__()方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好的对象引用直接赋值给self # 在类的内部,可以使用self来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法。 # 如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。 # 类中的方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个对象调用了实例方法。
class Cat: """这是一个猫类""" def __init__(self, x, y): # 重写了 __init__ 魔法方法 self.name = x self.color = y self.__age = 5 # 私有属性需要在类中做一个函数方法接口用来调用 # 访问私有属性 def get__age(self, age): self.__age = age # 修改私有属性 def set__age(self): self.__age+=1 return self.__age def __str__(self): return '小猫是:{},颜色是{}'.format(self.name, self.color) def __del__(self): print('__del__方法被调用了,删除此对象') def eat(self): return "%s爱吃鱼" % self.name def drink(self): return '%s爱喝水' % self.name """ tom = Cat() TypeError: __init__() missing 2 required positional argument: 'name','color' 这种写法在运行时会直接报错!因为 __init__ 方法里要求在创建对象时,必须要传递 name,color 属性,如果不传入会直接报错! """ # 创建对象时,必须要指定name属性的值 tom = Cat("Tom", "blue") tom.set__age() print(tom.get__sge()) del tom # 当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法。 # tom.eat() # 对象被删除,再次调用对象方法报错
5. 实例对象的私有属性和私有方法
类内部通过定义函数方法去访问实例对象的私有属性,也可定义函数方法去调用实例对象的私有方法
实例对象的私有属性:不能在类的外部被使用或直接访问
实例对象的私有方法:只能在类的内部调用 ,不能在类的外部调用。
#类内部通过定义公有方法去访问私有属性,也可定义公有方法去调用私有方法
class Person:
publicCount = 0 # 公开变量
def __init__(self, name, age):
self.name = name
self.age = age
self.__money = 2000 # __money 是私有变量,外部无法访问
# 定义了get_money 方法,在这个方法里获取到 __money
def get_money(self):
return self.__money # 内部可以访问 __money 变量
# 定义了set_money 方法,在这个方法里,可以修改 __money
def set_money(self, money):
self.__money = money
# 私有方法外部不能直接调用,只能在内部使用
def __foo(self):
print("这是私有方法")
def foo(self):
print("私有方法被调用了")
self.__foo() # 私有方法被调用
p = Person('王五', 21)
# 外部通过调用 get_money 和 set_money 这两个接口的公开方法获取和修改私有属性变量
print(p.get_money())
p.set_money(8000)
print(p.get_money())
# 外部通过访问公有方法的接口去调用私有方法
p.foo() # 正常输出
6.类方法、属性和静态方法的使用: 类属性只能通过类对象修改,不能通过实例对象修改 类属性也可以设置为 私有,前边添加两个下划线
class Person(object):
type = "人类" # 类属性
# 类方法,需要@classmethod去定义
@classmethod
def test(cls): # 如果一个函数只用到了类属性,可以定义成一个类方法
# 类方法中的cls参数不需要手动传参,会自动传参,
# cls指的是类对象,cls就是Person;
print("哈哈哈哈" + cls.type)
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self, food): # def创建的都是对象方法,self指的是实例对象
print(self.name + "正在吃" + food)
# 如果一个方法里没有用到实例对象的任何属性,可以将这个方法定义成staticmethod,
# 静态方法
@staticmethod
def demo():
print("hello")
p1 = Person("张三", 18)
# 实例对象调用类内公共方法eat(不是私有方法__eat),直接用 对象名.方法名(),
# 不需要传递self,会自动将实例对象名传递给self
print(p1.eat("红烧牛肉面"))
# 对象方法还可以用类名.方法名(),这种方式不会自动给self传参,
# 需要手动指定self,如 传p1给self
print(Person.eat(p1, "红烧牛肉面")) # Person类对象,类名
Person.demo()
p1.demo()
# 类方法可以使用实例对象和类对象调用
print(p1.test())
print(Person.test()) #哈哈哈哈人类
# 静态方法:既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。
# 即可以用实例对象.add() 也可以类对象.add() 去调用静态方法
# 适用于不创建实例对象的场景
class Calculator(object):
@staticmethod
def add(a, b):
return a + b
# 没有创建对象的必要, 直接使用类名.方法名(),
# c=Calculator()
# c.add(1,2)
Calculator.add(2, 3)
7.单例模式的设计:保证实例对象在全局的唯一性,(如 电脑的回收站)
1.思想:创建两个私有的类的属性 __instance_01=None, __is_first=True,利用类的私有属性只能通过类对象修改这一该特点;且在外部无法随意修改;
重写父类object的__new__方法:定义为类方法,获取类属性保证该类开辟的内存空间只此一块;
重写父类object的__init__方法,
2.单例模式:
# 单例设计模式:保证对象在全局里唯一;(电脑 回收站 唯一)
class Singleton(object):
# 类属性在类空间中;
# 如果 类属性和实例属性 同名,实例对象优先访问实例属性;
# 但 类属性只能通过类对象进行修改;
__instance1 = None
__is_first = True
# 定义为类方法,是为了获取类属性instance1
# 改为__instance1私有属性更好,使外部无法访问此类方法,无法随意修改;
@classmethod
def __new__(cls, *args, **kwargs):
if cls.__instance1 is None:
cls.__instance1 = object.__new__(cls) # 创建一个对象,申请内存,并把对象类型设置为cls
return cls.__instance1
# 改写__init__方法
def __init__(self, a, b):
# 类属性初始为true,__is_first是类属性,类属性可以用类对象或实例对象进行调用;
# 第一次执行if段后,在实例对象内存空间中就添加了__is_first这个属性,且为false,
# 后面的实例对象s2,s3首先会访问自己的实例属性 __is_first ,值为false,进行if判断,
# 所以之后的实例对象属性就无法覆盖第一次的属性值了
# 这样就保证了属性值的唯一
if self.__is_first:
self.a = a
self.b = b
self.__is_first = False # 在实例对象空间中添加了一个实例属性:__is_first
# 执行后,在实例对象空间中有 a,b,__is_first 三个属性
# s1=Singleton('恭恭','喜喜')
# s2=Singleton('发发','财财')
# 普通创建s1 和 s2实例对象;是两个不同的实例对象,有两块内存空间,如何让它变成一个对象呢?
# 或者说:创建了一个实例对象s1,第二个创建的只能用第一个对象的地址空间
# 如何实现呢?
# 1.调用__new__方法申请内存空间,创建实例对象时,内部自动申请的
# 2.s1,s2都申请了内存空间,改写__new__方法限制实例申请空间,只能申请一次
# 3. 3.1 如果不重写__new__方法,会去调用object里的__new__方法,并申请内存空间
# 3.2 重写__new__方法,就需要自己手动申请内存空间了
# 开辟一段空间存放s1,此时空间中有 '恭恭', '喜喜', __is_first=False
s1 = Singleton('恭恭', '喜喜')
# 当类创建s2时,发现__new__方法行不通,且自己共用的s1空间中有 __is_first=False该属性,然后拿去判断if,也行不通
# 此时 公共实例对象空间的属性值将不会再被改变,单例模式设计成功
s2 = Singleton('发发', '财财')
s3 = Singleton('平平', '安安')
print(id(s1), id(s2)) # 29069168 29069168
print(s1 is s2) # 此时为true,同一个对象,内存空间
# 同一块内存空间中:后面的值会覆盖前面的值,即此时 s1变为了 '发发','财财'
print(s1.a, s1.b)
3.练习示例:
# 示例 : 计数 创建了几个实例对象
class Person(object):
# count=0 # 定义为普通属性,
__count = 0 # 定义为私有属性,外部无法访问,要在内部做一个接口函数get_count
# 方法一:new方法中计数
def __new__(cls, *args, **kwargs):
cls.__count += 1
return object.__new__(cls)
# 方法二:init方法中计数
def __init__(self, a, b):
# Person.count += 1
self.a = a
self.b = b
@classmethod
def get_count(cls):
return cls.__count
p1 = Person('张三', 18)
p2 = Person('张四', 19)
p3 = Person('李五', 20)
8. 封装继承多态的使用:
封装:将类的属性或函数方法限制在类中使用,而外部无法调用。私有化就是必要的操作
继承:1. 在类与类之间人为的建立父子关系,父类的属性和方法子类可以使用;支持多继承,如果有不同的父类名,但有相同的方法,遵从先到先得的顺序,先继承挨得近的父类及其父类的父类;如果这条路线上没找到所需方法,就换另一条树枝;遵从深度优先继承,然后再广度搜索;
2. 类的私有属性和私有方法都不能通过对象直接访问,但可以在类内部使用;
类的私有属性和私有方法,都不会被子类继承;
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def sleep(self):
print(self.name + "正在睡觉")
class Dog(Animal):
def bark(self):
print(self.name + "正在狗叫")
class Student(Animal):
def study(self):
print(self.name + "正在学习")
class weapon(object):
def gutou(self):
print(self.name + "用骨头当武器")
class Xiaotianquan(Dog, weapon):
def fight(self):
print(self.name + "在打狗")
# Dog() 会先调用__new__方法,再调用 __init__方法
# 1.Dog()没有__new__方法,会查看父类是否重写了__new__方法
# 2.父类也没有__new__方法,就会去查找父类的父类,找到了object,有__new__方法
# 调用__init__方法,Dog类没有,就会去找Animal父类的__init__
d1 = Dog("大黄", 2) # 调用父类的__init__,需要传两个参数
print(d1.name)
d1.sleep()
d1.bark()
d2 = Xiaotianquan("哮天犬", 500)
d2.sleep()
d2.bark()
d2.fight()
d2.gutou()
# 私有属性和私有方法的继承特点: 父类中的私有属性方法不会被子类继承;
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
self.__money = 1000 # 父类的私有属性
def eat(self):
print(self.name + "正在吃东西")
def __test(self): # 父类的私有方法
print("我是Animal类里的test方法")
class Person(Animal):
def __demo(self):
print("我是Person里的私有方法")
def _Animal__test(self):
pass
p = Person("张三", 18)
print(p.name)
p.eat()
p._Person__demo() # 自己类里的定义的私有方法, 对象名._类名__私有方法名()
p._Animal__test() # 父类的方法可以通过 对象名._父类名__私有方法名() 调用
p._Animal__money # 父类的属性可以通过 对象名._父类名__私有属性名 调用
# 子类无法继承父类的私有属性和方法,
# print(p._person__test()) # 父类的私有方法,子类不能继承 'Person' object has no attribute '_person__test'
# print(p._Person__money) # 继承的子类无法获取父类的私有属性 'Person' object has no attribute '_Person__money'
# 子类重写父类的方法
# 1.子类的实现和父类的实现完全不一样,子类可以选择重写父类的方法
# 2.子类在父类的基础上又添加了新的功能;
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def sleep(self):
print(self.name + "正在睡觉")
class Student(Person):
def __init__(self, name, age, school):
# 再写一次属性麻烦,继承调用父类有的属性 super().__init__(name,age)
# self.name=name
# self.age=age
# 调用父类的两种方法:建议使用super()
# 1. 父类名.方法名(self,形参)
Person.__init__(self, name, age)
# 2. 使用super() 直接调用父类的方法
super().__init__(name, age)
self.school = school # 新加自己的属性
def sleep(self): # 重写该方法,调用的时候优先选取子类的同名方法
print(self.name + "学生不能在课堂上睡觉")
s = Student("herry", 15, "忻州市一中")
s.sleep()
print(Student.__mro__)
多态:一种技巧,提高代码灵活度;
# 多态的使用:
# 基于继承,通过子类重写父类的方法,达到不同的子类对象调用相同父类方法,
# 得到不同的结果
# 提高代码灵活度
# 不用多态的示例: 新增一个品类的狗 就要新建一种狗类,
# 代码构建冗余,不能灵活使用
class PoliceDog(object):
def attack_enemy(self):
print("警犬正在攻击敌人")
class BlindDog(object):
def lead_road(self):
print("导盲犬正在领路")
class DrugDog(object):
def search_drug(self):
print("缉毒犬正在搜查")
class Person1(object):
def __init__(self, name, dog):
self.name = name
self.dog = dog
def work_with_pd(self):
print(self.name + "正在工作")
self.dog.attack_enemy()
def work_with_bd(self):
self.dog.lead_road()
def work_with_dd(self):
self.dog.search_drug()
class Dog(object):
def work(self):
print("正在工作")
class PoliceDog(object):
def work(self):
print("警犬正在攻击敌人")
class BlindDog(object):
def work(self):
print("导盲犬正在领路")
class DrugDog(object):
def work(self):
print("缉毒犬正在搜查")
class Person1(object):
# 此时self.dog=bd
def __init__(self, name, dog):
self.name = name
self.dog = dog
def work_with_dog(self):
# 实例对象bd调用work()实例方法
self.dog.work()
bd = PoliceDog()
# 将实例对象bd作为参数传入Person类中
police = Person1("祁厅长", bd)
police.work_with_dog()
dd = DrugDog()
police = Person1("祁厅长", dd)
police.work_with_dog()