目录
目录
一、方法的动态性
在其他语言,可以定义多个重名的方法,只要保证方法签名——形参列表,唯一即可。但在python里面,方法的参数没有声明类型,参数的数量也可以由可变参数控制
所以,不要使用重名的方法,定义了多个同名函数之后,只有最后一个函数可以被识别调用。
方法的动态性:python是动态语言,我们可以动态得为类添加新方法,或者动态修改类已有的方法
class Person:
def work(self):
print("努力上班")
def play_game(a):
print("{0}谁在玩游戏".format(a))
Person.play = play_game #把方法直接赋为类Person里的类方法
def work2(s):
print("好好工作,努力上班!")
P = Person()
P.work()
P.play() #P.play()从本质上来说等同于Person.play_game(P)所以,只需要把函数赋给Person即可。
Person.work = work2 #更新了原来的类方法work,变成了work2()
P.work()
#好好工作,努力上班!
二、私有属性和私有方法(实现方法)
(一)私有属性
1、通常我们约定,两个下划线开头的属性是私有的,其他为公共属性;
2、类内部可以直接访问私有属性(方法);
3、类外部不能直接访问私有属性(方法);
4、类外部可以通过“__类名__私有属性(方法)名”访问私有属性(方法)
#公共属性
class Employee:
def __init__(self, name, age):
self.name = name
self.age = age
e = Employee("高宇星", 18)
print(e.name)
print(e.age)
#高宇星
#18
#私有属性
class Employee:
def __init__(self, name, age):
self.name = name
self.__age = age
e = Employee("高宇星", 18)
print(e.name)
print(e.age)
#AttributeError: 'Employee' object has no attribute 'age'
当属性变为私有时,不能直接访问,否则会报错
正确的操作方式如下:
#通过__命名私有属性,私有属性不能直接被外部访问,如果想要访问,必须通过_方法名__私有属性
class Employee:
def __init__(self, name, age):
self.name = name
self.__age = age
e = Employee("高宇星", 18)
print(e.name)
print(e._Employee__age)
#18
print(dir(e))
#['_Employee__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
(二)私有方法
在类内部调用自己的属性或方法,直接用self.__属性。
#测试私有方法
class Employee:
__Company = "华为"
def __init__(self, name, age):
self.name = name
self.__age = age #私有的属性
def __work(self): #定义一个私有的方法
print("好好学习,天天向上呀")
print("年龄:{0}".format(self.__age)) #在类的内部自己调用自己的方法,是完全可以的
print(Employee.__Company)
e = Employee("高宇星", 18)
print(e.name)
print(e._Employee__age)
#18
e._Employee__work()
三、@property装饰器
#测试@property的用法
class Employee:
@property #装饰器
def salary(self):
print("salary run ···")
return 10000
emp1 = Employee()
#emp1.salary()
#salary run ···
print(emp1.salary) #直接把方法当做属性来调用
#emp1.salary = 20000 #AttributeError: can't set attribute 会报错,不能给salary设置属性
当我们的属性可以直接被外部调用的时候,很容易出现录入错误数据并输出错误数据的现象
这个时候,为了解决这个问题,我们可以先将属性私有化
class Employee:
def __init__(self, name, salary):
self.name = name
self.__salary = salary
def get_salary(self):
return self.__salary
def set_salary(self, salary):
if 1000 < salary < 500000:
self.__salary = salary
else:
print("工资输入有误,薪水在1000到50000这个范围")
emp1 = Employee("高宇星", 30000)
print(emp1.get_salary())
#30000
emp1.set_salary(-290000)
print(emp1.get_salary())
#工资输入有误,薪水在1000到50000这个范围
emp1.set_salary(20000)
print(emp1.get_salary())
#20000
同时,也可以利用装饰器
1、将一个方法的调用方式变成“属性调用”;
2、一般用来给我们的属性设置get和set方法;
class Employee:
def __init__(self, name, salary):
self.name = name
self.__salary = salary
@property
def salary(self):
return self.__salary
@salary.setter
def salary(self, salary):
if 1000 < salary < 500000:
self.__salary = salary
else:
print("工资输入有误,薪水在1000到50000这个范围")
emp1 = Employee("高宇星", 30000)
print(emp1.salary)
#30000
emp1.salary = -2000
print(emp1.salary)
#工资输入有误,薪水在1000到50000这个范围
emp1.salary = 2000
print(emp1.salary)
#2000
四、面向对象的三大特征介绍
1、封装(隐藏)
隐藏对象的属性和实现细节,只对外提供必要的方法。通过“私有属性和私有方法”等方式来实现封装,没有严格的语法级别的“访问控制”,更多的是靠程序员的自觉实现
2、继承
(1)让子类继承父类的特性,提高了代码的重用性,是一种增量进化,是代码复用的重要手段
(2)一个子类可以继承多个父类
(3)语法格式:
class 子类(父类):
类体
如果没有指定父类,默认父类是object类
object类是python里的一个类,object是所有类的父类,里面定义了一些多有类共有的默认属性,例如__new__()
#测试继承的基本使用
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age #私有属性,私有的方法和属性,子类继承了但不能直接用
def say_age(self):
print("年龄:我也不清楚多大年纪")
class Student(Person):
def __init__(self, name, age, score):
Person.__init__(self, name, age)
#必须显示调用父类的初始化方法,不然解释器不会去调用,子类直接引用了父类的属性
self.score = score
#Student继承了Person,Person继承了object类
print(Student.mro())
#[<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
s = Student("高宇星", 18, 100)
s.say_age()
#年龄:我也不清楚多大年纪
#说明父类里的东西,子类里也可以使用
print(s.name) #直接调用了父类的代码
#高宇星
print(s._Person__age) #子类不能直接调用父类的私有属性
#18
print(dir(s))
#['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_age', 'score']
(4)类成员的继承和重写
成员继承:子类继承了父类除构造方法之外的所有成员
方法重写:子类可以重新定义父类的方法, 这样就覆盖父类的方法,也称为“重写”
#测试方法的重写
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age #私有属性,私有的方法和属性,子类继承了但不能直接用
def say_age(self):
print("我的年龄:", self.__age)
def say_introduce(self):
print("我的名字是{0}".format(self.name))
class Student(Person):
def __init__(self, name, age, score):
Person.__init__(self, name, age) #必须显示调用父类的初始化方法,不然解释器不会去调用,子类直接引用了父类的属性
self.score = score
def say_introduce(self):
'''重写了父类的方法'''
print("报告老师,我的名字是:{0}".format(self.name))
s = Student("高宇星", 18, 100)
s.say_age()
s.say_introduce()
'''
我的年龄: 18
我的名字是高宇星
'''
s = Student("高宇星", 18, 100)
s.say_age()
s.say_introduce()
#我的年龄: 18
#报告老师,我的名字是:高宇星
(5)object根类__dir__()
object.dir()可以查看对象的所有属性
通过类的方法mro()或者类的属性_mor_可以输出这个类的继承层次结构
object类是所有类的父类,所有的类都有object类的属性和方法
#测试方法的重写
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age #私有属性,私有的方法和属性,子类继承了但不能直接用
def say_age(self):
print("我的年龄:", self.__age)
def say_introduce(self):
print("我的名字是{0}".format(self.name))
class Student(Person):
def __init__(self, name, age, score):
Person.__init__(self, name, age) #必须显示调用父类的初始化方法,不然解释器不会去调用,子类直接引用了父类的属性
self.score = score
def say_introduce(self):
'''重写了父类的方法'''
print("报告老师,我的名字是:{0}".format(self.name))
s = Student("高宇星", 18, 100)
s.say_age()
s.say_introduce()
'''
我的年龄: 18
我的名字是高宇星
'''
s = Student("高宇星", 18, 100)
s.say_age()
s.say_introduce()
#我的年龄: 18
#报告老师,我的名字是:高宇星
obj = object
print(dir(object))
#['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
print(dir(s))
#['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_age', 'say_introduce', 'score']
(6)重写__str__()方法
object有一个__str__()方法,用于的返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮助我们查看到对象的信息
#测试object里面的__str__()方法
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return "名字是:{0}".format(self.name)
p = Person("高宇星")
print(p)
#<__main__.Person object at 0x000001C7AE9A20D0>,重写__str__()函数之前,str默认的信息
#名字是:高宇星
(7)多重继承
一个子类可以有多个直接父类
好处:可以使我们类具备多个“父类”的特点
坏处:类的整体层次异常复杂,尽量避免使用
#多继承
class A:
def aa(self):
print("aa")
class B:
def bb(self):
print("bb")
class C(A, B):
def cc(self):
print("cc")
c = C()
c.cc()
c.aa()
c.bb()
'''
cc
aa
bb
'''
(8)mro()函数
python支持多继承,如果多个父类有相同名字的方法,在没有指定父类的时候,解释器将“从左到右”按顺序搜索
mro():方法解析顺序。我们可以通过mro()方法来获得“类的层次结构”,方法解析顺序也是按照这个累的层次结构寻找的
#mro()方法的测试
class A:
def aa(self):
print("aa")
def say(self):
print("say AAA!")
class B:
def bb(self):
print("bb")
def say(self):
print("say BBB!")
class C(A, B):
def cc(self):
print("cc")
c = C()
print(C.mro()) #打印类的层次,这个mro函数是C大类的函数
c.say() #在mro层次结构里从左到右得找
'''
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
say AAA!
Process finished with exit code 0
'''
(9)通过super()获得父类定义,而不是父类对象
#测试super(),代表父类的定义,而不是父类对象
class A:
def say(self):
print("A:", self)
class B(A):
def say(self):
#A.say(self) #在B里面想调用A的say方法,通过super()也可以完成,可以达到同样的效果 super().say() #super()函数之后的函数方法里面不需要带self
super().say()
print("B:", self)
B().say()
'''
A: <__main__.B object at 0x000001584C7D4160>
B: <__main__.B object at 0x000001584C7D4160>
说明从B里面调用A的say方法成功了
'''
#整个过程相当于是父类方法的重写
3、多态
同一个 方法调用由于对象不同会产生不同的行为。
(1)多态是方法的多态,而不是属性的;
(2)多态存在两个必要条件,继承和方法重写
#多态,一个方法调用,根据不同的对象调用不同的方法!
class Man:
def eating(self):
print("饿了,吃饭了")
class Chinese(Man):
def eating(self):
print("中国人用筷子吃饭")
class English(Man):
def eating(self):
print("英国人用叉子吃饭饭")
class Indian(Man):
def eating(self):
print("印度人用右手吃饭")
def manEating(a): #全局函数,不属于任何一个模块
if isinstance(a, Man):
a.eating() #传进来的都是人,只要是man的子类都可以,根据传的对象类型不同,调用的方法也不一样
else:
print("不能吃饭!哼哼")
manEating(Chinese())
#中国人用筷子吃饭
manEating(English())
#英国人用叉子吃饭饭
五、运算符重载
class Person:
def __init__(self, name):
self.name = name
def __add__(self, other):
if isinstance(other, Person):
#只有同类型的才能相加,所以要判断一下other是不是属于Person里的对象
return "{0}——{1}".format(self.name, other.name)
else:
print("不是同类对象不能同类相加")
def __mul__(self, other):
if isinstance(other, int):
return self.name * other
else:
print("不是同类对象不能同类相乘")
p1 = Person("litterStar")
p2 = Person("高宇星")
x = p1.__add__(p2)
print(x)
#litterStar——高宇星
x = p1 + p2
print(x)
#litterStar——高宇星
print(p1.__mul__(3))
#litterStarlitterStarlitterStar
print(p1 * 3)
#litterStarlitterStarlitterStar
六、特殊属性
class A:
def aa(self):
print("aa")
def say(self):
print("say AAA!")
class B:
def bb(self):
print("bb")
def say(self):
print("say BBB!")
class C(A, B):
def cc(self):
print("cc")
def __init__(self, nn):
self.nn = nn
c = C(3)
print(dir(c))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'aa', 'bb', 'cc', 'say']
print(c.__dict__) #__dict__可以看到类的所有属性值,以字典的形式表达
#{'nn': 3}
print(c.__class__)
#<class '__main__.C'>
print(C.__bases__) #要指向大写的类对象,找类对象的基类,以元祖的形式返回
#(<class '__main__.A'>, <class '__main__.B'>)
print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
print(A.__subclasses__())
#[<class '__main__.C'>]
七、对象的浅拷贝和深拷贝
import copy
#测试对象的浅拷贝和深拷贝
class MobliePhone:
def __init__(self, cpu, screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("计算")
print("cpu对象:", self)
class Screen:
def show(self):
print("显示一幅美丽的画面")
print("screen对象", self)
#测试变量赋值
c1 = CPU()
c2 = c1
print(c1)
print(c2)
'''
<__main__.MobliePhone object at 0x000001D18754A400>
<__main__.MobliePhone object at 0x000001D18754A400>
'''
#浅复制
s1 = Screen()
m1 = MobliePhone(c1, s1)
m2 = copy.copy(m1) #需要导入copy模块
print(m1)
print(m2)
'''
<__main__.MobliePhone object at 0x000001CB031F85E0>
<__main__.MobliePhone object at 0x000001CB0330B040>
m1 和 m2 是完全不同的两个对象
'''
print(m1, m1.cpu, m1.screen)
print(m2, m2.cpu, m2.screen)
'''
<__main__.MobliePhone object at 0x00000191930885E0> <__main__.CPU object at 0x0000019193088640> <__main__.Screen object at 0x0000019193088100>
<__main__.MobliePhone object at 0x000001919319B040> <__main__.CPU object at 0x0000019193088640> <__main__.Screen object at 0x0000019193088100>
m1和m2虽然是两个对象,地址不同,但是使用的cpu和screen都是相同的
'''
#测试深复制
m3 = copy.deepcopy(m1)
print(m1, m1.cpu, m1.screen)
print(m3, m3.cpu, m3.screen)
'''
<__main__.MobliePhone object at 0x00000299F80B85E0> <__main__.CPU object at 0x00000299F80B8640> <__main__.Screen object at 0x00000299F80B8100>
<__main__.MobliePhone object at 0x00000299F8614F40> <__main__.CPU object at 0x00000299F8749C10> <__main__.Screen object at 0x00000299F8749C40>
cpu和screen都发生了变化,说明他们把子对象的子对象都进行了复制
'''
八、组合
Is—a关系,我们可以用继承,狗是动物
Has—a关系,我们可以使用组合,手机有cpu
它们都可以有代码调用的功能
#使用继承实现代码的复用
class A1:
def say_a1(self):
print("a1,a1,a1")
class B1(A1):
pass
b1 = B1()
b1.say_a1()
#a1,a1,a1
#同样的方法,使用组合来实现代码的复用
class A2:
def say_a2(self):
print("a2, a2, a2")
class B2:
def __init__(self, a): #当B2里面直接使用A2的属性,把A2的属性传入B2的参数
self.a = a
a2 = A2()
b2 = B2(a2)
b2.a.say_a2()
#测试has—a 的关系,使用组合
class MobliePhone:
def __init__(self, cpu, screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("计算")
print("cpu对象:", self)
class Screen:
def show(self):
print("显示一幅美丽的画面")
print("screen对象", self)
m = MobliePhone(CPU(), Screen())
m.cpu.caculate()
m.screen.show()
九、工厂模式
class CarFactory:
def create_car(self, broad):
if broad == "奔驰":
return Benz()
elif broad == "宝马":
return BMW()
elif broad == "比亚迪":
return BYD()
else:
print("未知品牌,无法创建")
class Benz:
pass
class BMW:
pass
class BYD:
pass
factory = CarFactory() #先建立工厂
c1 = factory.create_car("奔驰") #造车,让我的工厂造车
c2 = factory.create_car("比亚迪")
print(c1)
print(c2)
'''
<__main__.Benz object at 0x000001941106B490>
<__main__.BYD object at 0x000001941106B220>
只是有这个未知保留了,已经预留了车位,准备检车,但是由于各个车型还没有传入制造方法,所以只能看到其对象id
'''
十、单例模式
class MySingleton:
__obj = None #创建了一个类,类的属性为空 __obj用来描述对象的属性
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None: #如果类对象属性为空
cls.__obj = object.__new__(cls) #那就建立一个类属
return cls.__obj
def __init__(self, name):
为了使初始化方法制备调用一次
if MySingleton.__init_flag: #这里不可赋布尔值
print("init···")
self.name = name
MySingleton.__init_flag = False #初始的时候为True,改变其初始值为Flase,意味着初始化方法不会被调用两次
a = MySingleton("aa")
print(a)
b = MySingleton("bb")
print(b)
c = MySingleton("cc")
print(c)
d = MySingleton("dd")
print(d)
init···
<__main__.MySingleton object at 0x0000025376F7B220>
<__main__.MySingleton object at 0x0000025376F7B220>
<__main__.MySingleton object at 0x0000025376F7B220>
<__main__.MySingleton object at 0x0000025376F7B220>
实现了单例模式,切init方法只调用了一次
十一、单例模式和工厂模式的组合
#把工厂模式和单例模式的结合使用
a = "new start"
b = a.center(20, "*")
print(b)
class CarFactory:
__obj = None #创建了一个类,类的属性为空 __obj用来描述对象的属性
__init_flag = True
def create_car(self, broad):
if broad == "奔驰":
return Benz()
elif broad == "宝马":
return BMW()
elif broad == "比亚迪":
return BYD()
else:
print("未知品牌,无法创建")
def __new__(cls, *args, **kwargs):
if cls.__obj == None: #如果类对象属性为空
cls.__obj = object.__new__(cls) #那就建立一个类属
return cls.__obj
def __init__(self):
'''为了使初始化方法制备调用一次'''
if CarFactory.__init_flag: #这里不可赋布尔值
print("init CarFactory···")
CarFactory.__init_flag = False #初始的时候为True,改变其初始值为Flase,意味着初始化方法不会被调用两次
class Benz:
pass
class BMW:
pass
class BYD:
pass
factory = CarFactory() #先建立工厂
c1 = factory.create_car("奔驰") #造车,让我的工厂造车
c2 = factory.create_car("比亚迪")
print(c1)
print(c2)
print("嘿嘿嘿")
factory2 = CarFactory()
print(factory)
print(factory2)
'''
嘿嘿嘿
<__main__.CarFactory object at 0x000001B672F4B220>
<__main__.CarFactory object at 0x000001B672F4B220>
这两个工厂的对象都是同一个,实现了单例模式
'''