7月4日Python基础学习笔记——私有属性&私有方法、@property 装饰器、组合、工厂模式&单例模式等


前言

本文为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>在玩游戏
>>> 好好工作,努力上班!

三、私有属性&私有方法

要点:

  1. 两个下划线开头的属性是私有的(private),其他为公共的(public);
  2. 类内部可以访问私有属性(方法);
  3. 类外部不能直接访问私有属性(方法);
  4. 类外部可以通过 _类名__私有属性(方法)名 访问私有属性(方法)。
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)

多态是指同一个方法调用由于对象不同会产生不同的行为。要点:

  1. 多态是方法的多态,属性没有多态;
  2. 多态的存在有 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
  • 常见的特殊方法:
    常见的特殊方法- 各运算符对应的方法:
    运算符1
    运算符2
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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值