天天看点

Python面向对象之七:类成员

Python面向对象之七:类成员

一、按区域划分

按照类成员的区域划分,分为静态属性(变量)和动态属性(方法):

Python面向对象之七:类成员

1、静态属性(变量)

静态属性是指定义在类中的变量,用于表示类自身的属性。

Python面向对象之七:类成员

1、公有静态属性

class Person:

    name = '晴朗'  #公有静态属性

    def sleep(self):
        print(Person.name)

class Mother(Person):

    def temp(self):
        print(Person.name)


print(Person.name)  #类外部可以访问

per = Person()
per.sleep()    #本类的实例化对象内部可以访问

print(Mother.name)  #派生类外部可以访问

mother = Mother()
mother.sleep()   #派生类实例化对象内引用的父类方法可以访问
mother.temp()    #派生类实例化对象内引用的自己方法可以访问
           

分析:在公有静态属性中,类外部可以访问、本类的实例化对象内部可以访问、派派生类外部可以访问、派生类实例化对象内引用的父类方法可以访问、派生类实例化对象内引用的自己方法可以访问,就是哪里都能访问到!

2、私有静态属性

class Person:

    __name = '晴朗' #私有静态属性

    def sleep(self):
        print(Person.__name)

class Mother(Person):

    def temp(self):
        print(Person.__name)


print(Person.__name)   #不可以在类外部访问

per = Person()
per.sleep()    #类的实例化对象内部可以访问

print(Mother.__name)  #不可以派生类外部访问

mother = Mother()
mother.sleep()   #派生类实例化对象内部引用父类的方法可以访问

mother.temp()    #派生类实例化对象内部引用自己的方法不可以访问
           

分析:在私有静态属性中,仅在类内部(基类内部和派生类内部调用父类未重写的方法)可以访问。

3、公有对象属性

class Person:

    def __init__(self):
        self.name = '晴朗'


    def sleep(self):
        print(self.name)

class Mother(Person):

    def temp(self):
        print(self.name)


# print(Person.name)   #类外部不能访问

per = Person()
per.sleep()     #类的实例化对象内部可以访问
print(per.name) #类实例化对象可以访问

# print(Mother.name)  #派生类外部不可以访问

mother = Mother()
mother.sleep()   #派生类实例化对象内部引用父类的方法可以访问
mother.temp()    #派生类实例化对象内部引用自己的方法可以访问
print(mother.name) #派生类实例化对象可以访问
           

分析:在公有对象属性中,无论是基类对象还是派生类对象,都可以访问。

4、私有对象属性

class Person:

    def __init__(self):
        self.__name = '晴朗'


    def sleep(self):
        print(self.__name)

class Mother(Person):

    def temp(self):
        print(self.__name)

per = Person()
per.sleep()     #类的实例化对象内部可以访问
# print(per.__name) #类实例化对象无法访问

mother = Mother()
mother.sleep()   #派生类实例化对象内部引用父类的方法可以访问
# mother.temp()    #派生类实例化对象内部引用自己的方法无法访问
# print(mother.__name) #派生类实例化对象可以访问
           

分析:在私有对象属性中,仅在对象内部(基类对象内部和派生类对象内部调用父类未重写的方法)可以访问。

2、动态属性(方法)

动态属性是指定义在类中的方法,用于表示类自身的行为。

1、实例方法

第一个参数必须是实例对象,该参数名一般约定为“self”(也可以用别的,但是不建议),通过它来传递实例的属性和方法(也可以传类的属性和方法)。

1.1 公有实例方法

class Person:

    def sleep(self):
        print('in Person sleep')

    def eat(self):
        print('in Person eat')

    def fun(self):
        self.sleep()

class Mother(Person):

    def fun1(self):
        self.sleep()


per = Person()
per.sleep()  #基类实例化对象可以访问
per.fun()    #基类实例化对象内部可以访问

mother = Mother()
mother.sleep()  #派生类实例化对象可以访问
mother.fun1()    #派生类实例化对象内部可以访问
           

分析:基类和派生类都可以访问,无限制。

1.2 私有实例方法

class Person:

    def __sleep(self):
        print('in Person sleep')

    def __eat(self):
        print('in Person eat')

    def fun(self):
        self.__eat()

class Mother(Person):

    def fun1(self):
        self.__eat()


per = Person()
# per.sleep()  #基类实例化对象无法访问
per.fun()    #基类实例化对象内部可以访问

mother = Mother()
# mother.sleep()  #派生类实例化对象无法访问
# mother.fun1()    #派生类实例化对象内部无法访问
           

分析:仅在基类实例化对象内部可以访问,也就是说仅在类内部可以访问。

2、双下方法

双下方法是特殊方法,它是解释器提供的,由双下划线加方法名加双下划线,方法名的具有特殊意义的方法,双下方法主要是python源码程序员使用的,我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。

双下方法典型的代表就是构造方法:

Python中的构造方法是__init__函数,当实例化对象时被执行,它也是一种双下方法。

在Python中,子类如果定义了构造函数,而没有调用父类的,那么Python不会自动调用,也就是说父类的构造函数不会执行。

class Person:

    def __init__(self):
        print('in Person')

class Mother(Person):

    def __init__(self):
        print('in Mother')

mother = Mother()  #in Mother
           

3、类方法

使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法),可以被类和对象调用。

使用类方法统计对象创建的次数:

class Person:

    __num = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Person.addNum()

    @classmethod
    def addNum(cls):
        cls.__num += 1

    @classmethod
    def getNum(cls):
        return cls.__num

class Mingren(Person):
    pass

per1 = Person('小红', 12)
per2 = Person('小黄', 14)
per3 = Person('小绿', 16)

mingren1 = Mingren('老马', 55)
mingren2 = Mingren('小马', 36)
print(Person.getNum())  #5

           

分析:类方法就是将类本身作为对象进行操作的方法,其中的cls类似于self,cls就是类本身。

4、静态方法

使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法,可以使用类名直接调用。

静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系。静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。说白了就是一个记录的工具,不参加程序逻辑。

class Person:

    pass

    @staticmethod
    def temp():
        return '就是想吐槽一下,反正我与类和对象都无关'

    pass

print(Person.temp()) #类名可以调用

per = Person()
print(per.temp()) #实例化对象可以调用
           

分析:静态方法一般用于和类对象以及实例对象无关的代码。

二、property属性

Python 有一个概念叫做 property,它能让你在 Python 的面向对象编程中轻松不少。

property属性是通过使用@property装饰器或property函数将一个函数将变成类的一个属性,属性名为函数名,属性值为函数返回值。

property函数的参数:fget – 获取属性值的函数、fset – 设置属性值的函数、fdel – 删除属性值函数、doc – 属性描述信息

property属性的应用:

1、在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改:

class Person:

    age = 18

per = Person()
per.age = -18
print(per.name)  #-18
           

分析:age属性可以被随意修改,而且没有对修改值加限定,导致age属性不符合现实逻辑,造成数据极不安全。

2、为了不让age属性随意被修改,我们就要给属性修改添加限定,比如age属性代表的是对象的年龄,它不能为负数,也不能大于150:

class Person:

    __age = 18

    def getAge(self):
        return self.__age

    def setAge(self, age):
        if age < 0 or age >150:
            raise ValueError('年龄输入有误!')
        else:
            self.__age = age

per = Person()
print(per.getAge())  #获取age属性
per.setAge(30)   #修改age属性
print(per.getAge())   #获取age属性
           

分析:通过使用set和get方式,成功对age属性进行约束。

3、现在已经能在保证age属性安全的情况下对其赋值和查询了,但是每次赋值和查询都需要调用set和get方法,那么问题来了,假设一个完整程序已经完成之后才发现没有给age属性添加限制,该程序有一万个地方通过obj.age的方式调用了age属性,现在我们为了对age属性加以限制,把调用方式改成了obj.set(age)和obj.get(),那岂不是这一万个地方都要重新修改,这个工作量可想而知。为了解决这个问题,我们就要用到property属性:

class Person:

    def __init__(self, age = 18):
        self.__age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if age < 0 or age > 150:
            raise ValueError('年龄输入有误!')
        else:
            self.__age = age

per = Person()
print(per.age)  #获取age属性  18
# per.age = -20   #ValueError: 年龄输入有误!
per.age = 60
print(per.age)   #获取age属性  60
           

分析:现在age属性的调用方式还是obj.age,而且也给age属性添加了限制,保证了数据安全,这就完美了。

4、上面是property属性的装饰器写法,还有一种通过property函数来完成的写法:

class Person:

    def __init__(self, age = 18):
        self.__age = age

    def getAge(self):
        print('get放被调用!')
        return self.__age

    def setAge(self, age):
        print('set方法被调用!')
        if age < 0 or age > 150:
            raise ValueError('年龄输入有误!')
        else:
            self.__age = age

    age = property(getAge, setAge)

per = Person()
print(per.age)  #获取age属性  触发get方法
# per.age = -20   #ValueError: 年龄输入有误!
per.age = 60    #修改属性    触发set方法  
print(per.age)   #获取age属性   触发get方法
# 运行结果
# get放被调用!
# 18
# set方法被调用!
# get放被调用!
# 60
           

分析:通过上面的测试,可以得知只要通过obj.age获取age属性就会触发get方法,只要通过obj.age修改age属性就会触发set方法。

三、isinstace函数 与 issubclass函数

1、issubclass函数

issubclass() 方法用于判断参数 class 是否是类型参数 classinfo 的子类,侧重点在于父类和子类的关系。

语法:issubclass(class, classinfo)

参数:class:需要判断的类、 classinfo:对比的类

返回值:布尔值

例:

class M:
    pass

class A:
    pass

class B(A):
    pass

class C(B):
    pass

print(issubclass(B, A))  # 返回 True
print(issubclass(C, A))  # 返回 True
print(issubclass(C, M))  # 返回 False
           

2、isinstace函数

isinstance() 函数来判断一个对象是否是一个已知的类型,侧重点在于对象和类的关系。

语法:isinstance(object, classinfo)

参数:object – 实例对象、 classinfo – 可以是直接或间接类名、基本类型或者由它们组成的元组

返回值:布尔值

class M:
    pass

class A:
    pass

class B(A):
    pass

class C(B):
    pass

print(isinstance(B(), A))  # 返回 True
print(isinstance(C(), A))  # 返回 True
print(isinstance(B(), M))  # 返回 False
           

isinstance与type的区别

class M:
    pass

class A:
    pass

class B(A):
    pass

class C(B):
    pass

print(isinstance(A(), A))  # 返回 True
print(type(A()) == A)      # 返回 True
print(isinstance(B(), A))  # 返回 True
print(type(B()) == A)  # 返回 False
           

分析:

1、type() 不会认为子类是一种父类类型,不考虑继承关系,只会考虑本类对象是本类的类型。

2、isinstance() 会认为子类是一种父类类型,考虑继承关系。