文章目录
前言
本文为7月4日Python基础学习笔记,包含十六个章节:
- 方法的重载:如果在类体中定义了多个重名的方法,只有最后一个方法有效;
- 方法的动态性:可动态的添加新方法或修改已有的方法;
- 私有属性&私有方法;
- @property 装饰器;
- 实操作业;
- 面向对象的三大特征:封装、继承、多态;
- object 根类:dir() 查看对象属性、重写 __str()__ 方法;
- 多重继承:不建议使用;
- mro() 查看类的继承层次结构;
- super() 获得父类的定义;
- 特殊方法和运算符重载;
- 特殊属性;
- 对象的浅拷贝&深拷贝;
- 组合;
- 工厂模式;
- 单例模式(Singleton Pattern)。
一、方法没有重载
- Python 中没有方法重载;
- 如果在类体中定义了多个重名的方法,只有最后一个方法有效;
- 建议: 不要使用重名的方法!
二、方法的动态性
Python 是动态语言,我们可以:
- 动态的为类添加新的方法;
- 或者动态的修改类的已有的方法。
class Person:
def work(self):
print("努力上班!")
def play_game(s):
print("{0}在玩游戏".format(s))
def work2(s):
print("好好工作,努力上班!")
Person.play = play_game # 为类添加新方法
p = Person()
p.work()
p.play()
Person.work = work2 # 修改类已有的方法
p.work()
>>> 努力上班!
>>> <__main__.Person object at 0x00000225A134BB80>在玩游戏
>>> 好好工作,努力上班!
三、私有属性&私有方法
要点:
- 两个下划线开头的属性是私有的(private),其他为公共的(public);
- 类内部可以访问私有属性(方法);
- 类外部不能直接访问私有属性(方法);
- 类外部可以通过 _类名__私有属性(方法)名 访问私有属性(方法)。
class Employee:
__company = "SANY" # 私有类属性
def __init__(self, name, age):
self.name = name
self.__age = age # 私有实例属性
def say_company(self):
print("公司是:", Employee.__company) # 类内部可以直接访问私有属性
print(self.name, "的年龄是:", self.__age)
self.__work()
def __work(self): # 私有实例方法
print("好好工作,努力赚钱")
e = Employee("小李", 18)
print(e.name)
# print(e.age)
print(e._Employee__age) # 直接访问到私有实例属性
print(dir(e))
print(e._Employee__work()) # 直接调用私有实例方法
print(Employee._Employee__company) # # 直接访问到私有类属性
>>> 小李
>>> 18
>>> ['_Employee__age', '_Employee__company', '_Employee__work', '__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_company']
>>> 好好工作,努力赚钱
>>> None
>>> SANY
四、@property 装饰器
@property 可以将一个方法的调用方式变成属性调用,简单示例如下:
class Employee:
@property # 将一个方法的调用变成属性调用,不能设置属性
def salary(self):
print("计算中……")
return 10000
emp1 = Employee()
# print(emp1.salary())
print(emp1.salary)
>>> 计算中……
>>> 10000
@property 主要用于帮助我们处理属性的读操作、写操作。
若需要限制薪水必须为 1-10000 的数字。这时候,我们就需要通过 getter、setter 方法来处理:
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 1 < salary < 10000:
self.__salary = salary
else:
print("录入错误")
'''
def get_salary(self):
return self.__salary
def set_salary(self, salary):
if 1000<salary<50000:
self.__salary = salary
else:
print("录入错误")
'''
emp1 = Employee("小李", 30000)
#print(emp1.get_salary())
#emp1.set_salary(-20000)
#print(emp1.get_salary())
print(emp1.salary)
emp1.salary = 20000
emp1.salary = 5000
print(emp1.salary)
>>> 30000
>>> 录入错误
>>> 5000
五、实操作业
1、使用图文分析如下代码整个内存过程
2、设计一个名为 MyRectangle 的矩形类来表示矩形。这个类包含:(1) 左上角顶点的坐标:x,y;(2) 宽度和高度:width、height;(3) 构造方法:传入 x,y,width,height。如果(x,y)不传则默认是 0,如果 width和 height 不传,则默认是 100;(4) 定义一个 getArea() 计算面积的方法;(5) 定义一个 getPerimeter(),计算周长的方法;(6) 定义一个 draw()方法,使用海龟绘图绘制出这个矩形
import turtle as t
class MyRectangle:
def __init__(self, x, y, width, height):
self.cor_x = x
self.cor_y = y
self.width = width
self.height = height
def input(self):
if self.cor_x == '' and self.cor_y == '':
self.cor_x = 0
self.cor_y = 0
print("输入为 x={0}, y={1}, width={2}, height={3}".format(self.cor_x, self.cor_y, self.width, self.height))
else:
if self.width == '' and self.height == '':
self.width = 0
self.height = 0
print("输入为 x={0}, y={1}, width={2}, height={3}".format(self.cor_x, self.cor_y, self.width, self.height))
else:
print("输入为 x={0}, y={1}, width={2}, height={3}".format(self.cor_x, self.cor_y, self.width, self.height))
def getArea(self):
Area = self.width * self.height
print("面积为:", Area)
def getPerimeter(self):
Perimeter = 2 * (self.width + self.height)
print("周长为:", Perimeter)
def draw(self):
t.penup()
t.goto(self.cor_x, self.cor_y)
t.pendown()
t.forward(self.width)
t.right(90)
t.forward(self.height)
t.right(90)
t.forward(self.width)
t.right(90)
t.forward(self.height)
t.hideturtle()
t.done()
Rectangle = MyRectangle(2, 2, 300, 200)
Rectangle.input()
Rectangle.getArea()
Rectangle.getPerimeter()
Rectangle.draw()
>>> 输入为 x=2, y=2, width=300, height=200
>>> 面积为: 60000
>>> 周长为: 1000
六、面向对象的三大特征
Python 是面向对象的语言,也支持面向对象编程的三大特性:继承、封装(隐藏)、多态。
1、封装
隐藏对象的属性和实现细节,只对外提供必要的方法。
2、继承
继承可以让子类具有父类的特性。原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。语法格式:
class 子类类名(父类1[, 父类2, ……]):
- 如果在类定义中没有指定父类,则默认父类是 object 类;
- 定义子类时,必须在其构造函数中调用父类的构造函数。
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def say_age(self):
print(self.name, "的年龄是:", self.__age)
class Student(Person):
def __init__(self, name, age, score):
self.score = score
Person.__init__(self, name, age)
'''构造函数中包含调用父类构造函数。根据需要,不是必须。
子类并不会自动调用父类的__init__(),我们必须显式的调用它 '''
s1 = Student("小李", 18, 80)
s1.say_age()
print(dir(s1))
>>> 小李 的年龄是: 18
>>> ['_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']
(1)、类成员的继承和重写
子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为重写。
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def say_age(self):
print(self.name, "的年龄是:", self.__age)
def say_name(self):
print("我是:", self.name)
class Student(Person):
def __init__(self, name, age, score):
self.score = score
Person.__init__(self, name, age) # 构造函数中包含调用父类构造函数
def say_score(self):
print(self.name, "的分数是:", self.score)
def say_name(self): # 重写父类的方法
print("报告,我是:", self.name)
s1 = Student("小李", 18, 80)
s1.say_score()
s1.say_name()
s1.say_age()
>>> 小李 的分数是: 80
>>> 报告,我是: 小李
>>> 小李 的年龄是: 18
3、多态(polymorphism)
多态是指同一个方法调用由于对象不同会产生不同的行为。要点:
- 多态是方法的多态,属性没有多态;
- 多态的存在有 2 个必要条件:继承、方法重写。
class Animal:
def shout(self):
print("动物叫了一声")
class Dog(Animal):
def shout(self):
print("小狗汪汪汪")
class Cat(Animal):
def shout(self):
print("小喵喵喵喵")
def animalShout(a):
if isinstance(a, Animal): # isinstance: 用于判断对象是否是一个已知的类型
a.shout() # 传入的对象不同,shout 方法对应的实际行为也不同
animalShout((Dog()))
animalShout((Cat()))
>>> 小狗汪汪汪
>>> 小喵喵喵喵
七、object 根类
object 类是所有类的父类,因此所有的类都有 object 类的属性和方法。
1、dir() 查看对象属性
obj = object()
print((dir(obj)))
>>> ['__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__']
2、重写 str() 方法
用于返回一个对于对象的描述,对应于内置函数 str() 经常用于 print()方法,帮助查看对象的信息。
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def __str__(self):
'''将对象转化成一个字符串,一般用于 print 方法'''
return "名字:{0},年龄{1}".format(self.name, self.__age)
p = Person("小李", 18)
print(p)
>>> 名字:小李,年龄18
八、多重继承
Python 支持多重继承,一个子类可以有多个直接父类。 但是,这样会被“类的整体层次”搞的异常复杂,尽量避免使用。
class A:
def aa(self):
print("aa")
class B:
def bb(self):
print("bb")
class C(B, A):
def cc(self):
print("cc")
c = C()
c.cc()
c.bb()
c.aa()
>>> cc
>>> bb
>>> aa
九、mro() 查看类的继承层次结构
通过类的方法 mro() 或者类的属性 mro 可以输出这个类的继承层次结构。
# 测试 mro() 方法,查看类的继承层次结构
class A:
pass
class B(A):
pass
class C(B):
pass
print(C.mro())
>>> [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
十、super() 获得父类的定义
如果想要获得父类的方法,我们可以通过super()来做。
class A:
def say(self):
print("A: ", self)
print("say AAA")
class B(A):
def say(self):
super().say() # 通过super()调用父类的方法
print("say BBB")
b = B()
b.say()
>>> A: <__main__.B object at 0x0000017583C08820>
>>> say AAA
>>> say BBB
十一、特殊方法和运算符重载
Python 的运算符实际上是通过调用对象的特殊方法实现的。
a = 20
b = 30
c = a + b
d = a.__add__(b)
print("c=", c)
print("d=", d)
>>> c= 50
>>> d= 50
- 常见的特殊方法:
- 各运算符对应的方法:
class Person:
def __init__(self, name):
self.name = name
def __add__(self, other):
if isinstance(other, Person):
return "{0}--{1}".format(self.name, other.name)
else:
return "不是同类对象,无法相加"
def __mul__(self, other):
if isinstance(other, int):
return self.name * other
else:
return "不是同类对象,无法相乘"
p1 = Person("小李")
p2 = Person("大李")
x = p1 + p2
print(x)
print(p1 * 3)
>>> 小李--大李
>>> 小李小李小李
十二、特殊属性
class A:
pass
class B:
pass
class C(B, A):
def __init__(self, nn):
self.nn = nn
def cc(self):
print("cc")
c = C(3)
print(dir(c))
print(c.__dict__) # 对象的属性字典
print(c.__class__) # 对象所属的类
print(C.__bases__) # 类的基类元组(多继承)
print(C.mro()) # 类层次结构
print(A.__subclasses__()) # 子类列表
>>>
['__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__', 'cc', 'nn']
{'nn': 3}
>>> <class '__main__.C'>
>>> (<class '__main__.B'>, <class '__main__.A'>)
>>> [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
>>> [<class '__main__.C'>]
十三、对象的浅拷贝&深拷贝
- 浅拷贝: 拷贝时,对象包含的子对象内容不拷贝。因此,源对象和拷贝对象会引用同一个子对象;
- 深拷贝: 递归拷贝对象中包含的子对象。
import copy
class MobilePhone:
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)
c = CPU()
s = Screen()
m = MobilePhone(c, s)
m2 = copy.copy(m) # m2 是新拷贝的另一个手机对象
print(m, m2)
m.cpu.calculate()
m2.cpu.calculate()
m.screen.show()
m2.screen.show() # m2 和 m 拥有了一样的 cpu 对象和 screen 对象
print("深拷贝…………")
m3 = copy.deepcopy(m)
m.cpu.calculate()
m3.cpu.calculate()
m.screen.show()
m3.screen.show() # m3 和 m 拥有不一样的 cpu 对象和 screen对象
>>> <__main__.MobilePhone object at 0x000002394845FE80> <__main__.MobilePhone object at 0x000002394845FB80>
>>> 计算中……
>>> CPU 对象: <__main__.CPU object at 0x000002394845FFD0>
>>> 计算中……
>>> CPU 对象: <__main__.CPU object at 0x000002394845FFD0>
>>> 画面
>>> Screen 对象: <__main__.Screen object at 0x000002394845FF40>
>>> 画面
>>> Screen 对象: <__main__.Screen object at 0x000002394845FF40>
>>> 深拷贝…………
>>> 计算中……
>>> CPU 对象: <__main__.CPU object at 0x000002394845FFD0>
>>> 计算中……
>>> CPU 对象: <__main__.CPU object at 0x000002394845DED0>
>>> 画面
>>> Screen 对象: <__main__.Screen object at 0x000002394845FF40>
>>> 画面
>>> Screen 对象: <__main__.Screen object at 0x000002394845DE40>
十四、组合
除继承外,组合也能实现一个类拥有另一个类的方法和属性, 即手机拥有 CPU。
class MobilePhone:
def __init__(self, cpu, screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("计算中……")
class Screen:
def show(self):
print("画面")
c = CPU()
s = Screen()
m = MobilePhone(c, s) # 通过组合,我们也能调用 cpu 对象的方法。相当于手机对象间接拥有了 cpu 的方法
m.cpu.calculate()
m.screen.show()
>>> 计算中……
>>> 画面
十五、工厂模式
实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
class CarFactory:
def createCar(self, brand):
if brand == "奔驰":
return Merc()
elif brand == "宝马":
return BMW()
elif brand == "比亚迪":
return BYD()
else:
return "未知品牌,无法创建"
class Merc:
pass
class BMW:
pass
class BYD:
pass
factory = CarFactory()
c1 = factory.createCar("奔驰")
c2 = factory.createCar("宝马")
print(c1)
print(c2)
>>> <__main__.Merc object at 0x0000022765D6B2E0>
>>> <__main__.BMW object at 0x0000022765A18820>
十六、单例模式(Singleton Pattern)
- 核心作用: 确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
- 单例模式只生成一个实例对象,减少了对系统资源的开销。
class MySingleton:
__obj = None
__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
a = MySingleton("aa")
print(a)
b = MySingleton("bb")
print(b)
>>> <__main__.MySingleton object at 0x000001D2F866B2E0>
>>> <__main__.MySingleton object at 0x000001D2F866B2E0>