Python学习-Day9-面向对象基础(下)

一、私有化属性

1.概述

  • 前面学习面向对象过程中,修改类属性都是直接通过类名修改的。如果有些重要属性不想让别人随便修改,或者防止意外修改,该怎么办?

  • 为了更好的保存属性安全,即不能随意修改,将属性定义为私有属性,添加一个可调用的方法去访问。

  • 使用私有属性的场景:

  1. 把特定的属性隐藏起来,不想让类的外部直接调用

  1. 我想保护该属性,不想让属性的值随意的改变

  1. 保护这个属性,不想让派生类【子类】去继承该属性

2.语法

  • 两个下划线开头,声明该属性为私有,不能在类的外部被使用直接访问

  • 示例

class Person(object): 
__age = 18 # 定义一个私有化属性,属性名字前面加两个下划线 

3.特性

  • 私有化属性不能在类外面访问。【不管是类属性还是实例属性都是如此】

  • 私有化属性可以在类里面访问,修改。

  • 子类不能继承私有化属性。

  • 小结

  1. 私有化的【实例】属性,不能在外部直接访问,可以在类的内部随意的使用

  1. 子类不能继承父类的私有化属性【只能继承父类公共的属性和行为】

  1. 在属性名前面直接加‘__’就可以私有化

二、私有化方法

1.概述

私有化方法跟私有化属性概念一样,有些重要的方法,不允许外部调用,防止子类意外重写,把普通的方法设置成私有化方法。

2.语法

  • 私有化方法,即在方法名前面加两个下划线。

  • 示例

class A(object): 
    ## 在方法前面加两个 __ 下划线,变成私有化方法 
    def __myname(self): 
        print('xiaoming’) 
    #普通方法 
    def myname(self): 
        print(xiaoming) 
    
a = A() 
a.myname() # 正常调用 
a.__myname() # 调用私有化方法,报错 

3.特性

  • 私有化方法一般是类内部调用,子类不能继承,外部不能调用。

  • 单下划线、双下划线、头尾双下划线说明

  1. _xxx 前面加一个下划线,以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能使用from xxx import * 的方式导入。

  1. __xxx__ 前后两个下滑线,魔法方法,一般是python自有,开发者不要创建这类型的方法。

  1. xxx_ 后面单下滑线,避免属性名与python关键字冲突

class Animal :
    def __eat(self):
        print('吃东西')
        pass
    def run(self):
        self.__eat()#内部可调用私有化方法
        print('跑')
        pass
    pass
class Bird (Animal):
    pass
b1=Bird()
#b1.__eat() 外部不能调用
b1.run()

三、Property属性

1.概述

  • 上节我们讲了访问私有变量的话,一般写两个方法一个访问,一个修改,由方法去控制访问。

class Person(object): 
    def __init__(self): 
        self.__age = 18 # 定义一个私有化属性,属性名字前加连个 __ 下滑线 
    
    def get_age(self): # 访问私有类属性 
        return self.__age 
    
    def set_age(self,age): # 修改私有属性 
        if age < 0: 
            print('年龄不能小于0’) 
        else: 
            self.__age = age 
  • 这样给调用者的感觉就是调用了一个方法,并不是访问属性。我们怎么做到让调用者直接以访问属性的方式,而且我们又能控制的方式提供给调用者?

  • Python中有一个被称为属性函数(property)的小概念,它可以做一些有用的事情。

2.实现方式一

  • 类属性,即在类中定义值为property对象的类属性

  • 示例:给age属性设置值时,会自动调用setage方法,获取age属性值时,会自动调用getage方法。

class Person(object):
    def __init__(self):
        self.__age = 18 # 定义一个私有化属性,属性名字前加连个 __ 下滑线
    def get_age(self): # 访问私有实例属性
        return self.__age
    def set_age(self,age): # 修改私有实例属性
        if age < 0:
            print('年龄不能小于0')
        else:
            self.__age = age
    #定义一个类属性 实现通过直接访问属性的形式去访问私有属性
    age = property(get_age,set_age) # 定义一个属性,当对这个age设置值时调用set_age,
    # 当获取值时调用get_age
    # 注意:必须是以get,set开头的方法名,才能被调用
p1=Person()
p1.age=15#相当于调用 set_age
print(p1.age)#相当于调用get_age

3.实现方式二

  • 装饰器,即在方法上使用装饰器

class Person(object):
    def __init__(self):
        self.__age = 18 # 定义一个私有化属性,属性名字前加连个 __ 下滑线

    @property # 使用装饰器对age进行装饰,提供一个getter方法
    def age(self): # 访问私有实例属性
        return self.__age

    @age.setter # 使用装饰器进行装饰,提供一个setter方法
    def age(self, age): # 修改私有实例属性
        if age < 0:
            print('年龄不能小于0')
        else:
            self.__age = age

xiaoming = Person()
xiaoming.age = 15
print(xiaoming.age)

四、__new__方法

1.概述

  • __new__方法的作用是,创建并返回一个实例对象,如果__new__只调用了一次,就会得到一个对象。继承自object的新式类才有new这一魔法方法。

  • 注意事项:

  1. __new__ 是在一个对象实例化的时候所调用的第一个方法

  1. __new__至少必须要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供 ,其他的参数是用来直接传递给 __init__ 方法

  1. __new__ 决定是否要使用该 __init__ 方法,因为 __new__ 以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__ 没有返回实例对象,则 __init__ 不会被调用

  1. 在__new__ 方法中,不能调用自己的__new__ 方法,即:return cls.__new__ (cls),否则会报错(RecursionError: maximum recursion depth exceeded:超过最大递归深度)

2.使用方式

  • 示例1

class A(object): #默认继承超类

    def __init__(self): 
        print("__init__执行了") 
    
    def __new__(cls, *args, **kwargs): #静态方法
        print("__new__ 执行了") 
        return object.__new__(cls) # 调用父类的new方法 【真正创建实例】

a = A() 
  • 输出结果

  • 默认

class Animal(object):
    def __init__(self):
        self.color='红色'
        pass
    #如果不重写,默认情况如下
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)
    pass
tigger=Animal()#实例化的过程会自动调用__new__去创建实例
#在新式类中__new__才是真正的实例化方法,为类提供外壳制造出实例框架,
# 然后调用该框架内的构造方法__init__进行丰满操作
#比喻建房子__new__方法负责开发地皮,打地基,并将原料存放在工地,
# 而__init__负责从工地取材料建造地皮开发图纸规定的大楼,负责细节设置,最终完成

五、单例模式

1.概述

  • 单例模式是常用设计模式的一种,单例就比如我们打开电脑的回收站,在系统中只能打开一个回收站,也就 是说这个整个系统中只有一个实例重复打开也是使用这个实例

  • 简单的说就是不管创建多少次对象,类返回的对象都是最初创建的,不会再新建其他对象。

2.实现步骤-1

  • 利用类属性保存初次创建的实例对象,第二次实例化的时候判断类属性是否有保存实例对象,如果有就返回类属性保存的,如果没有就调用父类__new__方法创建新的实例对象。

#创建一个单例对象【基于__new__去实现的[也是推荐的一种]】
class DataBaseClass(object):
    def __new__(cls, *args, **kwargs):
        # cls._instance=cls.__new__(cls) 不能使用自身的new方法
        # 容易造成一个深度递归,应该调用父类的New方法
        if not hasattr(cls,'_instance'):#如果不存在就开始创建
            cls._instance=super().__new__(cls, *args, **kwargs)
        return cls._instance
    pass
d1=DataBaseClass()
print(id(d1))
d2=DataBaseClass()
print(id(d2))
d3=DataBaseClass()
print(id(d3))
  • 输出结果:我们可以看到,无论创建多少次,都是只实例化一个对象

class SingleCase(object):
    __instance = None # 保存实例对象
    def __init__(self,name,age):
        print(name,age)
    def __new__(cls, *args, **kwargs):
        # 如果类属性 __instance 的值为None,那么新建一个对象
        # 如果类属性值不为None 返回 __instance 保存的对象
        if not cls.__instance:
            cls.__instance = super(SingleCase,cls).__new__(cls) # 调用父类__new__方法生成一个实例对象
            return cls.__instance
        else:
            return cls.__instance

obj1 = SingleCase('xiaoming',18)
obj2 = SingleCase('xiaoming',118)
print(id(obj1)) # id相等,说明实例化两次对象,实际上都是同一个对象
print(id(obj2))

3.实现步骤2

class SingleCase(object):
    __instance = None # 保存实例对象
    __isinit = True # 首次执行init方法标记
    def __init__(self,name,age):
        if SingleCase.__isinit:
            self.name = name
            self.age = age
            SingleCase.__isinit = False
    def __new__(cls, *args, **kwargs): # 如果类属性 __instance 的值为None,那么新建一个对象
                # 如果类属性值不为None 返回 __instance 保存的对象
        if not cls.__instance:
            cls.__instance = super(SingleCase, cls).__new__(cls) # 调用父类__new__方法生成一个实例对象
            return cls.__instance
        else:
            return cls.__instance
obj1 = SingleCase('xiaoming',18)
obj2 = SingleCase('luban',118)
print(id(obj1))
print(id(obj2))
print(obj1.age)
print(obj2.age) # 年龄都输出18。说明init值初始化了一次

六、错误与异常处理

1.概述

  • 有时候代码写错了,执行程序的时候,执行到错误代码的时候,程序直接终止报错,这是因为Python检测到一个错误时,解释器就无法继续执行了,出现了错误的提示,这就是"异常"。

  • 示例:变量b没有定义,直接打印变量b,会报异常。

2.语法格式

try: 

    可能出现错误的代码块 

except: 

    出错之后执行的代码块 

else: 

    没有出错的代码块 

finally: 

    不管有没有出错都执行的代码块

3.try ... except 语句

  • 可能出错的代码放到try里面,except可以指定类型捕获异常。except里面的代码是捕获到异常时执行,将错误捕获,这样程序就不会因为一段代码包异常而导致整个程序崩溃

  • 示例1:捕获ZeroDivisionError异常

# 0不能做除数,1/0必然报错 
1/0 

try: 
    1/0 # 将可能会报错的代码放到try里面 
except: 
pass 

try: 
    1/0 # 将可能会报错的代码放到try里面 
except ZeroDivisionError as e: # 捕获异常,ZeroDivisionError 异常类型 
    print(e) 
  • 示例2:

try:
    print(b) #捕获逻辑的代码

except NameError as  msg:#捕获到错误,才会在这里执行
    print(msg)
print('*'*10)
print('HA'*10)
  • 输出结果

  • 示例3:except在捕获错误异常的时候,是要根据具体的错误类型来捕获的

try:
    #print(b) #捕获逻辑的代码
    li=[1,2,3]
    a = 10/0
    print(li[10])

#except在捕获错误异常的时候,是要根据具体的错误类型来捕获的
except NameError as  msg:#捕获到错误,才会在这里执行
    print(msg)
except IndexError as msg:
    print(msg)
except ZeroDivisionError as msg:
    print(msg)
print('*'*10)
print('HA'*10)
print(dir(Exception))#查看所有的异常类型
  • 示例4:万能类型【当出现所有类型不确定的情况下,可使用如下的方法】

如果在运行时发生异常,解释器会查找相应的异常捕获类型,如果在当前函数里面没有找到,他会将异常传递给上层的调用的函数,看能否处理,如果在最外层没有找到,解释器就开始报错,程序崩溃

try:
    #print(b) #捕获逻辑的代码
    li=[1,2,3]
    a = 10/0
    print(li[10])

#except在捕获错误异常的时候,是要根据具体的错误类型来捕获的
except Exception as  result:#捕获到错误,才会在这里执行,万能类型,捕获所有类型异常
    print(result)
print('*'*10)
print('HA'*10)
print(dir(Exception))#查看所有的异常类型

def A(s):
    return 10/int(s)
def B(s):
    return A(s)*2
def main():
    try:
        A('0')
        pass
    except Exception as msg:
        print(msg)
main()
#不需要在每个可能出错的地方去捕获,只要在合适的层次去捕获错误就可以了,这样就大大减少了我们写try--except的麻烦

4.try ... except ... else语句

  • 没有捕获到异常时才执行else语句

  • 示例:

  • 代码示例

try:
    print('我是没有错误')
    pass
except Exception as msg:
    print(msg)
else:
    print('当try里面没有出现异常的情况下,我才会执行')
  • 结果

5.try ... except ... finally语句

  • 代码实例

try :
    int('fffff')
    pass
except Exception as msg:
    print(msg)
finally:
    print('释放文件的资源、数据库连接的资源等等')
    print('不管有没有出错都执行的代码块')

6.try嵌套

7.异常传递机制

  • 如果多个函数嵌套调用,内层函数异常,异常会往外部传递,直到异常被抛出,或被处理。

  • 示例1:

  • 输出结果:

  • 示例2

  • 输出结果

8.自定义异常

  • 自定义异常,都要直接或间接继承ErrorException类

  • 由开发者主动抛出自定义异常,在python中使用raise关键字

  • 代码示例

class TooLongMyException(Exception):
    def __init__(self,length):
        '''

        :param length: 长度
        '''
        self.len=length
    def __str__(self):
        return '你输入的长度'+str(self.len)+',超过长度'
    pass
def name_test():
    name=input('请输入姓名')
    try:
        if len(name)>5:
            raise TooLongMyException(len(name))
        else:
            print(name)
            pass
        pass
    except TooLongMyException as result:
        print(result)
        pass
    finally:
        print('执行完毕')
name_test()
  • 输出结果

七、动态添加属性和方法

1.概述

  • 动态语言:运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。如php,JavaScript,python都是动态语言,C,C#,java是静态语言。

  • 所以python可以在程序运行过程中添加属性和方法

2.动态添加属性

  • 2-1.运行中给对象添加属性【实例属性】

  • 代码

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
        pass
    def __str__(self):
        return '{}今天{}岁了'.format(self.name,self.age)
    pass
zyh=Student('张燕花',25)
zyh.weight=80 #动态添加给实例对象
print(zyh)
print(zyh.weight)
  • 2-2运行中给类添加属性

  • 代码

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
        pass
    def __str__(self):
        return '{}今天{}岁了'.format(self.name,self.age)
    pass
zm=Student('张明',15)
Student.major='湖南工商大学'
print(zm.major)
  • 2-3动态添加实例方法

动态添加实例方法需要使用types

  • 代码

import types #添加方法的库
def dymicMethod(self):
    print('{}今天{}岁了'.format(self.name,self.age))
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
        pass
    def __str__(self):
        return '{}今天{}岁了'.format(self.name,self.age)
    pass
zm=Student('张明',15)
zm.printInfo=types.MethodType(dymicMethod,zm)
zm.printInfo()#调用动态绑定的方法

  • 2-4 给类绑定类方法和静态方法

使用方式:类名.方法名 = xxxx

  • 代码

@classmethod
def classTest(self):
    print('这是一个类方法')
@staticmethod
def staticTest():
    print('这是一个静态方法')
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
        pass
    def __str__(self):
        return '{}今天{}岁了'.format(self.name,self.age)
    pass
Student.TestMethod=classTest #绑定类方法
Student.TestMethod()
Student.StaticMethod=staticTest#绑定静态方法
Student.StaticMethod()

八、__slots__属性

1.概述

  • python是动态语言,在运行的时候可以动态添加属性。如果要限制在运行的时候给类添加属性,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性。

  • 只有在__slots__变量中的属性才能被添加,没有在__slots__变量中的属性添加失败。可以防止其他人在调用类的时候胡乱添加属性或方法。__slots__属性子类不会继承,只有在当前类中有效

2.使用方式

  • 代码

class Student(object):
    # 限制我们要添加的实例属性
    #可以看到类实例属性已经不能随意创建不在__slots__定义的属性
    #同时还可以节约资源
    __slots__ = ('name','age')
    def __str__(self):
        return '{}...{}'.format(self.name,self.age)
    pass
xw=Student()
xw.name='小王'
xw.age=20
#xw.score=96 没有在限制范围内,所以报错
print(xw)
#当子类未声明__slots__,此时,子类是可以随意添加
class subStudent(Student):
    pass
z1=subStudent()
z1.gender='男'
print(z1.gender)#说明子类可以随意添加,并没有延续父类中的slots属性

class subStudent1(Student):#子类声明了slots时,继承了父类的slots,
    # 也就是子类的slots属性范围为【子类】+【父类】
    __slots__ = ()
    pass

九、知识小结

  • 私有化属性

两个下划线开头,声明该属性为私有,不能在类的外部使用直接访问

  • 私有化方法

私有化方法,即在方法名前面两个下划线

  • Property属性

类属性,即在类中定义值为property对象的类属性

装饰器,即在方法上使用装饰器

  • __new__方法

_new_方法的作用是,创建并返回一个实例对象

  • 单例模式

不管创建多少次对象,类返回的对象都是最初创建的,不会再新建其他对象。

  • 错误与异常处理

try: 可能出现错误的代码块

except: 出错之后执行的代码块

else: 没有出错的代码块

finally: 不管有没有出错都执行的代码块

  • Python动态添加属性和方法

在程序运行过程中添加属性和方法

  • __slots__方法

在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性

十、课后问答题

1、Python中new方法作用是什么?

用来创建实例对象的,只有继承了【object】的话,才有这个方法

2、什么是单例模式?单例模式适用于什么场景?

要求一个类,有且只有一个实例,并且提供了一个全局的访问点

场景:日志插入logger的操作、网站计数器、权限验证模块、系统回收站、数据库连接池

3、私有化方法与私有化属性在子类中能否继承?

不能

4、在Python中什么是异常?

异常就是程序在执行过程中发生的错误

5、Python中是如何处理异常的。

根据异常的类型去处理

6、Python中异常处理语句的一般格式,可以使用伪代码的形式描述。

try:

正常操作

except:

.........

else:

........

finally:

.......

7、__slots__属性的作用

限制属性的随意输入

节省内存空间

8、私有化属性的作用?

保护数据

9、在类外面是否能修改私有属性。

不能,通过方法实现,属性函数property,

10、如果一个类中,只有指定的属性或者方法能被外部修改。那么该如何限制外部修改。

对数据进行私有化设定

十一、课后实操题

1、编写一段代码以完成下面的要求

定义一个Person类,类中要有初始化方法,方法中要有人的姓名,年龄两个私有属性.

提供获取用户信息的函数.

提供获取私有属性的方法.

提供可以设置私有属性的方法.

设置年龄的范围在(0-120)的方法,如果不在这个范围,不能设置成功.

'''
1、编写一段代码以完成下面的要求
定义一个Person类,类中要有初始化方法,方法中要有人的姓名,年龄两个私有属性.
提供获取用户信息的函数.
提供获取私有属性的方法.
提供可以设置私有属性的方法.
设置年龄的范围在(0-120)的方法,如果不在这个范围,不能设置成功.
'''
class Person:
    def __init__(self,n,a):
        '''
        
        :param n: 姓名
        :param a:年龄 
        '''
        self.__name=n
        self.__age=a
    def __str__(self):
        return '{}的年龄是{}'.format(self.__name,self.__age)
    def GetName(self):
        return self.__name
    def GetAge(self):
        return self.__age
    def SetName(self,name):
        self.__name=name
    def SetAge(self,age):
        if(age>=0 and age <=120):
            self.__age=age
        else:
            print('不在(0-120)范围内,设置失败')

2、请写一个单例模式

class Singal(object):
    flag=None
    sig_instance=None
    def __new__(cls, *args, **kwargs):
        if  not cls.flag:
            cls.flag=True
            cls.sig_instance=object.__new__(cls, *args, **kwargs)
            return cls.sig_instance
        else:
            return cls.sig_instance
s1=Singal()
s2=Singal()
print(id(s1))
print(id(s2))

3、创建一个类,并定义两个私有化属性,提供一个获取属性的方法,和设置属性的方法。利用property 属性给调用者提供属性方式的调用获取和设置私有属性方法的方式。

class Animal:
    def __init__(self,n,a):
        '''

        :param n: 姓名
        :param a: 年龄
        '''
        self.__age=a
        self.__name=n
    @property
    def name(self):
        return self.__name
    @name.setter
    def name(self,name):
        self.__name=name

    @property
    def age(self):
        return self.__name
    @age.setter
    def age(self, age):
        self.__age = age
    def __str__(self):
        return '{}的年龄是{}'.format(self.__name,self.__age)
cat=Animal('猫',2)
print(cat)
cat.name='加菲猫'
cat.age=3
print(cat)

4、创建一个Animal类,实例化一个cat对象,请给cat对象动态绑定一个run方法,给类绑定一个类属性colour,给类绑定一个类方法打印字符串'ok'。

'''
4、创建一个Animal类,实例化一个cat对象,
请给cat对象动态绑定一个run方法,
给类绑定一个类属性colour,
给类绑定一个类方法打印字符串'ok'。
'''
import types
class Animal:
    pass
def run(self):
    print('动态绑定run方法')
@classmethod
def calTest(self):
    print('ok')
cat=Animal()
cat.run1=types.MethodType(run,cat)
cat.run1()
print('绑定类属性-------')
Animal.color='橘色' #动态绑定类属性
print(cat.color)
#绑定类方法
Animal.calTest=calTest
Animal.calTest()

5、观察下面代码,定义了一个私有属性,并提供了两个一个set方法和一个get方法,请改下面代码利用propetry属性方式提供给外部调用set和get方法。

class Person(object): 
    def __init__(self): 
        self.__age = 18 # 定义一个私有实例属性 
    def get_age(self): 
        return self.__age # 访问私有实例属性 
    def set_age(self,age): 
        self.__age = age # 修改私有实例属性 
  • 修改后

class Person(object):
    def __init__(self):
        self.__age = 18 # 定义一个私有实例属性
    def get_age(self):
        return self.__age # 访问私有实例属性
    def set_age(self,age):
        self.__age = age # 修改私有实例属性
    age=property(get_age,set_age)
p1=Person()
p1.age=15
print(p1.age)

6、Python是一门动态语言,那么如何在程序执行过程给实例dog添加一个实例方法,请给下面代码中添加一个实例方法。

class Animal: 
    def __init__(self): 
        print('init') 
    def eat(self): 
        print('吃草') 

dog = Animal() 
  • 修改后

import types
class Animal:
    def __init__(self):
        print('init')
    def eat(self):
        print('吃草')

dog = Animal()
def run(self):
    print('跑')
dog.run1=types.MethodType(run,dog)
dog.run1()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜菜小林然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值