Python面向对象之七:类成员
一、按区域划分
按照类成员的区域划分,分为静态属性(变量)和动态属性(方法):
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL31keOl3aE1UMNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzIjM4QTNyADMxAjMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
1、静态属性(变量)
静态属性是指定义在类中的变量,用于表示类自身的属性。
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() 会认为子类是一种父类类型,考虑继承关系。