面向对象进阶一(成员(变量、方法、属性),组合或嵌套)
一、类的成员
类的成员共分为三类:变量、方法、属性。下面我们来一一学习。
二、变量
变量分为:
1)实例变量(又称字段)
a)公有实例变量(公有字段)
b)私有实例变量(私有字段)
2)类变量(又称静态字段)
a)公有类变量(公有静态字段)
b)私有类变量(私有静态字段)
分析下面示例,并了解实例变量和类变量:
# ######示例一(类变量和实例变量) class Foo: country = "中国" # 类变量(静态字段) def __init__(self, name): self.name = name # 实例变量(字段) def func(self): pass obj1 = Foo("alex") obj2 = Foo("sylar") print(obj1.name) # alex print(Foo.country) # 或print(obj1.name) 结果为:中国
上述示例一得如下准则:
实例变量(字段)访问时,使用对象访问,即obj1.name
类变量(静态字段)访问时,使用类访问,即Foo.country(实在不方便时,才使用对象访问)
# 易错点--通过类的某个对象改变类变量的值,该类的其他对象不会受影响,通过类改变类变量,该类的所有对象都会跟着改变 obj1.country = "美国" print(obj1.country) # 美国 print(obj2.country) # 中国 Foo.country = "美国" print(obj1.country) # 美国 print(obj2.country) # 美国
问题一:什么时候使用类变量?
当所有对象中有共同的字段,且要改都改,要删都删时,可以将实例变量(字段)提取到类变量(静态变量)。
# ######示例二(私有实例变量(私有字段)) class Foo: def __init__(self,name): # 规则:公有实例变量前加两个下划线(变量成员修饰符)就是私有实例变量 self.__name = name # 私有实例变量(私有字段) self.age = 123 def func(self): print(self.__name) obj = Foo('alex') print(obj.age) # age是公有实例变量 可以访问 结果为:123 # obj.__name # name是私有实例变量 无法直接访问 obj.func() # 找一个内部人:func, 让func帮你执行私有 __name 结果为:alex
# ######示例三(私有类变量(私有静态变量)) class Foo: # 规则:公有类变量前加两个下划线(变量成员修饰符)就是私有类变量 __country = "中国" # 私有类变量(私有静态变量) def func(self): # 内部调用 print(self.__country) print(Foo.__country) # 推荐使用类来使用静态变量 # print(Foo.country) # 报错 外部无法使用私有类变量 obj = Foo() obj.func() # 结果为: # 中国 # 中国
# ######示例四(验证该类的子类也不能访问该类的私有变量) class Base(object): __secret = "受贿" class Foo(Base): def func(self): print(self.__secret) print(Foo.__secret) obj = Foo() obj.func() # 报错 AttributeError: 'Foo' object has no attribute '_Foo__secret'
三、方法
方法分为:
1)实例方法
a)公有实例方法
b)私有实例方法
2)静态方法
a)公有静态方法
b)私有静态方法
3)类方法
a)公有类方法
b)私有类方法
分析下面示例,并了解实例方法、静态方法、类方法:
# ######示例一(实例方法) class Foo(object): def __init__(self, name): self.name = name
# 实例方法 def func(self): print(self.name) # 使用了对象中封装的值 obj = Foo('alex') obj.func()
# ######示例二(静态方法 @staticmethod 参数可有可无) class Foo(object): def __init__(self, name): self.name = name
# 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法 @staticmethod def display(a1,a2): return a1 + a2 print(Foo.display(1,3)) # 4
静态方法总结:
1)定义时:方法上方写@staticmethod、方法参数可有可无;
2)执行时:推荐用 类.方法名(),也可以用对象.方法名(),但是不推荐;
3)什么时候写静态方法:无需调用对象中封装的值时;
# ######示例三(类方法 @classmethod 参数至少有一个cls) class Foo(object):
# 类方法,自动传递cls,cls就是这个类 @classmethod def show(cls, x1, x2): print(cls, x1, x2) # 执行类方法 Foo.show(1, 8) # <class '__main__.Foo'> 1 8
类方法总结:
1)定义时:方法上方写@classmethod、方法至少有一个cls参数;
2)执行时:类名.方法名() 注意:默认会将当前类传到参数中,也可用 对象名.方法名() 但不推荐;
3)什么时候写类方法:如果在方法中会使用到当前类,那么就可以使用类方法;
面试题:静态方法/类方法和实例方法的区别?
定义时:
静态方法/类方法:方法上方分别要加@staticmethod/@classmethod;
实例方法:方法上方不用加东西;
调用时:
静态方法/类方法:通过 类名.方法名() 调用;
实例方法:通过 对象名.方法名() 调用;
应用场景:
静态方法:在方法内部不会用到对象封装的数据时;
类方法:在方法内部不会用到对象封装的数据,且会用到当前类时;
实例方法:在方法内部需要用到对象封装的数据时;
同变量类似,方法也有私有方法,规则分别是在公有方法名前加两个下划线(方法成员修饰符),如下:
# ######示例四(私有实例方法、私有静态方法、私有类方法) class Foo(object): def __init__(self, name): self.name = name def __fun1(self): print("私有实例方法",self.name) @staticmethod def __display(): print('私有静态方法') @classmethod def __func(cls): print(cls, "私有类方法") obj = Foo("alex") # obj.__fun1() # 无法访问 # Foo.__display() # obj.__display() # 类和对象都无法访问 # Foo.__func() # 无法访问
四、属性
属性是通过实例方法改造出来的,属性也分为公有属性和私有属性,看如下有关属性的示例:
# ######示例一 class Foo(object):
@property # 属性 def start(self): return 1 @property # 属性 def end(self): return 10 obj = Foo() print(obj.start) # 调用时不用加括号 结果为:1 print(obj.end) # 调用时不用加括号 结果为:10 # 在公有属性方法名前加两个下划线就是私有属性,不再举例
属性总结:
1)定义时:方法上方写@property、方法参数只有一个self;
2)调用时:无需加括号,直接写 对象.方法 即可;
3)应用场景:对于简单的方法,当无需传参且有返回值时,可以使用@property;
class Pagenation(object): """ 处理分页相关的代码 """ def __init__(self, data_list, page, per_page_num = 10): """ 初始化 :param data_list: 所有的数据 :param page: 当前要查看的页面 :param per_page_num: 每页默认要显示的数据行数 """ self.data_list = data_list self.page = page self.per_page_num = per_page_num @property def start(self): """ 计算索引的起始位置 :return: """ return (self.page-1) * self.per_page_num @property def end(self): """ 计算索引的结束位置 :return: """ return self.page * self.per_page_num def show(self): """ 打印一页的内容 :return: """ result = self.data_list[self.start:self.end] for row in result: print(row) data_list = [] for i in range(1, 901): data_list.append('alex-%s' % i) while True: page = int(input('请输入要查看的页码:')) obj = Pagenation(data_list,page) obj.show()
五、组合或嵌套(建模)
这里的嵌套不是专业的词汇,是我们用大白话解释的。面向对象中的嵌套其实就是对象的相互嵌套,看如下一个对象嵌套的示例:
class School(object): # 定义一个学校类 def __init__(self, name, address): self.name = name self.address = address def speech(self): print('讲课') # 创建三个学校对象 obj1 = School('北京校区', '昌平区') obj2 = School('上海校区', '浦东新区') obj3 = School('深圳校区', '南山区') class Teacher(object): # 定义一个老师类 def __init__(self, name, age, salary): self.name = name self.age = age self.__salary = salary self.school = None # 创建三个老师对象 t1 = Teacher('李杰', 19, 188888) t2 = Teacher('sylar', 18, 60) t3 = Teacher('女神',16, 900000) # 给老师分配校区 t1.school = obj1 # t1对象的school变量是一个obj1对象 t2.school = obj1 t3.school = obj2 # 查看t1老师,所在的校区名称/地址 print(t1.school.name) print(t1.school.address) print(t1.name) print(t1.age) t1.school.speech()