4.4-类的成员


类的成员

类分为两块区域:

  1. 第一部分:静态字段;
  2. 第二部分:方法部分;

每个区域详细划分:

class A:
    name = '黑色利穆'   # 静态变量(静态字段)
    __name = '白色利穆' # 私有静态变量(私有静态字段)

    def __init__(self, name, age):  # 特殊方法
        self.name = name        # 对象属性(普通字段)
        self.__age = age        # 私有对象属性(私有普通字段)
    
    def func1(self):            # 普通方法
        pass

    def __func2(self):          # 私有方法
        pass
    
    @classmethod                # 类方法
    def class_func(cls):
        '''定义类方法,至少有一个cls参数'''
        print('类方法')
    
    @staticmethod               # 静态方法
    def static_func():
        '''定义静态方法,无默认参数'''
        print('静态方法')
    
    @property                   # 属性
    def prop(self):
        pass

类的私有成员

对于每一个类的成员而言都有两种形式:

  1. 公有成员,在任何地方都能访问;
  2. 私有成员,只有在类的内部才能访问;

私有成员和公有成员的访问限制不同:

静态字段(静态属性):

  1. 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问;
  2. 私有静态字段:仅类内部可以访问;

公有静态属性(字段)

class A:
    name = '公有静态字段'
    def func(self):
        print(A.name)

class B(A):
    def show(self):
        print(A.name)

print(A.name)       # 类访问
obj = A()
obj.func()          # 类内部访问
obj_son = B()
obj_son.show()      # 派生类中可以访问

私有静态属性(字段)

class A:
    __name = '私有静态字段'
    def func(self):
        print(A.__name)

class B(A):
    def show(self):
        print(A.__name)

# print(A.__name)     # 不可在外部访问
obj = A()
# obj.__name          # 不可在外部访问
obj.func()          # 类内部可以访问
# obj_son = B()
# obj_son.show()      # 不可在派生类中访问

普通字段(对象属性)

  1. 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问;
  2. 私有普通字段:仅类内部可以访问;

公有对象属性

class A:
    def __init__(self):
        self.foo = '公有字段'

    def func(self):
        print(self.foo)     # 类内部访问

class B(A):
    def show(self):
        print(self.foo)     # 派生类中访问

obj = A()
print(obj.foo)              # 通过对象访问
obj.func()                  # 类内部访问

obj_son = B()
obj_son.show()              # 派生类中访问

私有对象属性

class A:
    def __init__(self):
        self.__foo = '私有字段'

    def func(self):
        print(self.__foo)     # 类内部访问

class B(A):
    def show(self):
        print(self.__foo)     # 派生类中访问

obj = A()
# print(obj.__foo)              # 通过对象访问    不行
obj.func()                  # 类内部访问     可以

# obj_son = B()
# obj_son.show()              # 派生类中访问        不行

方法

  1. 公有方法:对象可以访问;类内部可以访问;派生类可以访问;
  2. 私有方法:仅类内部可以访问;

公有方法

class A:
    def __init__(self):
        pass
    def add(self):
        print('in A')

class B(A):
    def show(self):
        print('in B')
    def func(self):
        self.show()

obj = B()
obj.show()      # 通过对象访问
obj.func()      # 类内部访问
obj.add()       # 派生类访问

私有方法

class A:
    def __init__(self):
        pass
    def __add(self):
        print('in A')

class B(A):
    def __show(self):
        print('in B')
    def func(self):
        self.__show()

obj = B()
# obj.__show()        # 不能通过对象访问
obj.func()      # 类内部可以访问
# obj.__add()     # 派生类不可以访问

总结

对于这些私有成员来说,只能在类内部使用,不能在类的外部以及派生类中使用;

ps:私有成员可以通过 对象._类__属性名 方式访问,但是绝对禁止的。

为什么可以通过 对象._类__属性名 访问?因为在类的创建时,如果遇到了私有成员(包括私有静态字段,私有普通字段,私有方法)都会将其保存在内存时自动在前边加上 _类名.

类的其他成员

其他成员:类方法

方法包括:普通方法,静态方法,和类方法。三种方法在内存中都属于类,但是调用方式不同;

实例方法

第一个参数必须是实例对象,该参数名一般为self,通过它来传递实例的属性和方法(也可以传递类的属性和方法);

调用:只能由实例对象调用;

实例方法就是类的实例能够使用的方法;

类方法

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

调用:实例对象和类的对象都可以调用;

'''
需求:现在有一个班级类和学生类;
执行班级人数增加的操作,或得班级的总人数;
学生类继承班级类,每实例化一个学生,班级总人数增加;
最后,定义一写学生,或得班级中的总人数;
'''
class Stu:
    __num = 0
    def __init__(self, name, age):
        self.name = name
        self.age = age
        Stu.addNum()

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

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

a = Stu('黑色利穆', 25)
b = Stu('色利穆', 26)
c = Stu('利穆', 27)
d = Stu('穆', 28)
print(Stu.getNum())

静态方法

使用装饰器@staticmethod,参数随意,没有self和cls参数,但是方法体重不能使用类或实例的任何属性和方法;

调用:实例对象和类对象都可以调用;

静态方法是类中的函数,不需要实例。静态方法主要用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的,单独的函数,仅仅托管于某个类的名称空间中,方便使用和维护。

# 定义一个关于时间的类,其中有一个获取当前时间的函数
import time
class TimeTest():
    def __init__(self, hour, minute, second):
        self.hour = hour
        self.minute = minute
        self.second = second

    def test(self):
        return f'{self.hour}时{self.minute}分{self.second}秒'

    @staticmethod
    def showTime():
        return time.strftime('%H:%M:%S', time.localtime())

print(TimeTest.showTime())
t = TimeTest(10,10,10)
print(t.test())

如上,使用了静态方法(函数),方法体中并没有使用(也不能使用)类或实例的属性(方法)。若要或得当前时间的字符串时,并不一定需要实例化对象,此时对于静态方法而言,所在类更像是一种名称空间;

属性

将方法伪装成属性

property是一种特殊的属性,访问它时会执行一段(函数)然后返回值

'''
BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86
'''
class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)

p1 = People('黑色利穆', 78, 1.72)
print(p1.bmi)

使用property的原因:

将一个类的函数定义成特性后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的。这种特性的使用方式遵循了统一访问的原则;

# 根据属性的访问特点,分别将三个方法定义为对同一个属性的:获取,修改,删除
class Foo:
    @property
    def AAA(self):
        print('get')

    @AAA.setter
    def AAA(self, value):
        print('set')

    @AAA.deleter
    def AAA(self):
        print('del')

# 只有在属性AAA定义property后,才能定义AAA.setter, AAA.deleter
f = Foo()
f.AAA           # get
f.AAA = 'bbb'   # set
del f.AAA       # del

或者

# 根据属性的访问特点,分别将三个方法定义为对同一个属性的:获取,修改,删除
class Foo:
    def get_AAA(self):
        print('get')

    def set_AAA(self, value):
        print('set')

    def delete_AAA(self):
        print('del')

    AAA = property(get_AAA, set_AAA, delete_AAA)       # 内置property三个参数与get,set,delete一一对应

# 只有在属性AAA定义property后,才能定义AAA.setter, AAA.deleter
f = Foo()
f.AAA           # get
f.AAA = 'bbb'   # set
del f.AAA       # del

练习

class Googs():
    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Googs()
print(obj.price)        # 获取商品价格
obj.price = 200         # 修改商品价格
print(obj.price)        # 获取修改后价格的折扣价格
del obj.price           # 删除商品价格

双下方法

特殊方法,由解释器提供,由双下划线加方法名加双下划线 的具有特殊意义的方法,双下方法主要是python源码程序员使用;开发中尽量不要使用双下方法

isinstace 与 issubclass

# isinstance(a,b) 判断a是否是b类(或b类的派生类)实例化的对象
class A:
    pass
class B(A):
    pass
obj = B()
print(isinstance(obj, B))   # True
print(isinstance(obj, A))   # True
# issubclass(a, b)      判断a类是否是b类(或b的派生类)的派生类
class A:
    pass
class B(A):
    pass
class C(B):
    pass
print(issubclass(B, A))     # True
print(issubclass(C, A))     # True

思考:那么list,str,tuple,dict这些类与Iterble类的关系?

from collections import Iterable
print(isinstance([1,2,3], list))        # True
print(isinstance([1,2,3], Iterable))    # True
print(issubclass(list, Iterable))       # True
# 如上,这些可迭代的数据类型,list,str,tuple,dict等,都是Iterable的子类

元类type

按照python的一切皆对象理论,类也是一个对象,那么类这个对象是如何实例化来的呢?

print(type('abc'))
print(type(True))
print(type(100))
print(type([1, 2, 3]))
print(type({'name': '黑色利穆'}))
print(type((1,2,3)))
print(type(object))

class A:
    pass

print(isinstance(object, type))
print(isinstance(A, type))

type元类是获取该对象从属于的类,而type类比较特殊;python原则:一切皆对象,其实类也可以理解为对象,而type元类又称作构建类,python中大多数内置的类(包括object)以及自己定义的类,都是由type元类创造的;

而type类与object类之间的关系比较特殊:object是type类的实例,而type类是object类的子类。

posted @ 2020-12-07 18:40  黑色利穆  阅读(117)  评论(0编辑  收藏  举报