面向对象 2
类的成员
变量
实例变量(字段)
类变量(静态字段)
class Foo(object): country = 'china' #类变量 def __init__(self, name): self.name = name #实例变量 self.age = 30 def func(self): print(Foo.country) print(self.name) obj1 = Foo('zhangsan') obj2 = Foo('lisi')
print(Foo.country) #类变量可以直接使用类名进行调用,无需实例化对象 obj1.func() obj2.func()
类变量和实例变量的区别:
1. 定义方式不同:
类变量必须在定义类的时候创建并赋值,程序编译后就存在内存当中了。
实例变量必须定义在__init__构造函数中,可以通过构造函数的参数进行传参赋值,也可以直接进行赋值,定义实例变量需要在变量前加self,只有实例化对象后,实例变量才会在内存中被创建。
2.调用方式不同:
类变量可以不用实例化对象,直接通过类名进行调用,也可以通过实例化后的对象进行调用。(在调用类变量时尽量使用类名去调用)
实例变量必须实例化对象后,通过对象进行调用
class Foo(object): country = 'china' def __init__(self, name): self.name = name self.age = 30 def func(self): print(Foo.country) print(self.name) obj1 = Foo('zhangsan') obj2 = Foo('lisi') print(Foo.country) print(obj1.country) print(obj2.country) obj1.country = 'USK' print(Foo.country) print(obj1.country) print(obj2.country) Foo.country = 'UK' print(Foo.country) print(obj1.country) print(obj2.country)
#china
#china
#china
#china
#USA
#china
#UK
#USA
#UK
这里我们说下为什么类变量也可以被对象调用,要知道无论是实例变量还是类变量都是变量,当使用对象去调用类变量时其实有一个隐式的值传递的过程,要知道实例变量在定义的时候都是前面有self的,当对象调用类变量时,然而这个对象的初始化的数据中是没有这个类变量的,那为什么还是可以成功调用呢?所以这里的调用可以理解成有一个隐式的obj.country = Foo.country 的过程,而这里的 obj.country = 'USA',相当于给obj1.country赋予了新的值,不在执行Foo.country了,下次在调用country时就优先到自己的对象中去取值,所以这就是为什么当Foo.country = 'UK'时,obj1.country的值还是USA,由于obj2.country还是引用的类变量,所以会跟着Foo.country变。
总结:
1.准则:
实例变量(字段)访问时,使用对象访问,即: obj1.name
类变量(静态字段)访问时,使用类方法,即: Foo.country (实在不方便时,才使用对象)
2.什么时候使用类变量
当所有对象中有共同的字段时且要改都改要删都删时,可以将 实例变量(字段) 提取到 类变量(静态字段)
方法
实例方法
静态方法
类方法
class Foo(object): # 构造方法 def __init__(self, name): self.name = name # 实例方法 def func1(self): print(self.name) # 静态方法 @staticmethod def func2(a, b): return a + b # 类方法 @classmethod def func3(cls, a, b): print(cls) return a + b obj = Foo('小明') obj.func1() print(Foo.func2(10, 20)) print(Foo.func3(100, 200))
区别:
1.定义方式:
实例方法定义时,最少有一个参数,第一个参数必须时self,而且方法内部需要使用对象封装的值。
静态方法定义时,方法使用@staticmethod装饰,参数可有可无,当方法内部无需使用对象封装的值的时候,就应该定义为静态方法。
类方法定义时,方法使用@classmethod装饰,最少又一个参数,第一个参数必须是cls,属于静态方法的升级版,在需要在方法中使用到当前类的情况下定义。
2.调用方式:
实例方法必须在对象实例化后,使用对象名进行调用。
静态方法和类方法可以直接使用类名进行调用(建议方式),也可以使用对象名进行调用。
属性
class Foo(object): def __init__(self, a, b): self.a = a self.b = b @property def add(self): return self.a + self.b @property def mul(self): return self.a * self.b obj = Foo(10, 20) print(obj.add) print(obj.mul)
当实例方法只有一个self参数(不需要传入其他参数),可以使用@property装饰器将该方法变成一个属性,在调用可以不在需要括号,结果为方法的返回值。
使用场景:对于简单的方法,当无需传参且有返回值时,可以使用 @property
class Show(object): def __init__(self, page, data_list, per_page_num=10): self.page = page self.per_page_num = per_page_num self.data_list = data_list @property def start(self): return (self.page - 1) * self.per_page_num @property def end(self): return (self.page - 1) * self.per_page_num + self.per_page_num def show_paeg(self): for item in self.data_list[self.start:self.end]: print(item) data_list = ['alex_%s' % i for i in range(1, 901)] while True: page = int(input('请输入要查看的页码:')) obj = Show(page, data_list) obj.show_paeg() if page == 1000: break
成员修饰符
class Foo(object): city = '杭州' #公有类变量 __country = '中国' #私有类编 def __init__(self, name, age, salary): self.name = name self.age = age self.__salary = salary def func1(self): #公有实例方法 print(self.name) print(self.__salary) def __func2(self): #私有实例方法 print(self.name) @staticmethod #公有静态方法 def func3(): print('python') @staticmethod #私有静态方法 def __func4(): print('python') @classmethod #公有类方法 def func5(cls): print(cls) print('python') @classmethod #私有类方法 def __func6(cls): print(cls) print('python') @property #公有属性 def func7(self): return self.__salary @property #私有属性 def __func8(self): return self.name
类的所有成员在名称前面加上"__"就变成了私有成员,私有成员无法直接被外界访问,无法被子类继承。
类的组合/建模(嵌套)
class Collage(object): def __init__(self, name, addr): self.name = name self.addr = addr @staticmethod def teach(): print('讲课') obj1 = Collage('北大', '北京') obj2 = Collage('浙大', '杭州') obj3 = Collage('武大', '武汉') class Teacher(object): def __init__(self, name, age): self.name = name self.age = age self.school = None t1 = Teacher('张三', 50) t2 = Teacher('李四', 60) t1.school = obj1 t2.school = obj2 print(t1.school.name) print(t2.school.name) print(t1.school.addr) print(t2.school.addr)
当两个类之间在逻辑上有一定联系,又不是继承关系时,可以使用嵌套的方式建立两个类之间的联系。
在类中主动调用其他类的成员
在某些特定的环境下,可能有这样一种情况,需要在自己的类中调用其他类的方法,有以下两种方式
class Foo(object): country = '中国' def __init__(self, name): self.name = name def f1(self): print(self.name) class Foo3(object): def __init__(self, name): self.name = name def f1(self): obj_foo = Foo(self.name) obj_foo.f1()
在自己类中创建一个其他类的对象,通过这个对象来调用其类中的方法,这种适用与两个类之间没有任何联系(继承关系)时使用
还有一种情况时,子类重写父类中的方法,也就是在子类中创建和父类同名的方法时,想调用父类中的同名方法,可以用super关键字进行调用。
class Foo(object): country = '中国' def __init__(self, name, age, salary, address): self.name = name self.age = age self.salary = salary self.address = address class Foo4(Foo): def f1(self): super().f1() print(self.salary) print(self.address)
这种情况适用子类觉得父类中的方法无法满足自己的需求时,重写父类的这个方法(定义一个跟父类同名的方法),但是父类中存在的代码又不想再写一遍了,由于继承的特性,默认是只能执行自己的方法的,如果想也执行父类中的方法,就可以用super来调用父类中的同名方法。
类中的特殊成员
员 class SpecialMember(object): country = '中国' """ 此类说明类中的特殊成员,下面方法中obj为类的实例化对象 """ def __init__(self, name): ''' 初始化方法,在创建对象时会执行 :param name: ''' self.name = name self.dic = {} def __call__(self, *args, **kwargs): ''' obj(1,2,3,a=4,b=5)会触发此方法 :param args: :param kwargs: :return: ''' return args, kwargs def __getitem__(self, item): ''' obj[123] 会触发此方法 :param item: :return: ''' return item def __setitem__(self, key, value): ''' obj['key'] = value 会触发此方法 :param key: :param value: :return: ''' self.dic[key] = value def __delitem__(self, key): ''' del obj[key] 会触发此方法 :param key: :return: ''' del self.dic[key] def __add__(self, other): ''' obj1+obj2 会触发此方法 :param other: :return: ''' return self.name + other.name def __enter__(self): ''' with obj as f: f接收此方法的返回值,会触发此方法,需要和exit一起使用。 :return: ''' print('with一个对象时会触发') return self.name def __exit__(self, exc_type, exc_val, exc_tb): ''' :param exc_type: :param exc_val: :param exc_tb: :return: ''' print('配合__enter__使用') def __new__(cls, *args, **kwargs): ''' 构造方法 在创建类时默认继承object中的该方法,该方法会创建一个新的空对象 :param args: :param kwargs: :return: ''' return object.__new__(cls) def __str__(self): ''' 不写的话,默认是继承object中的__str__方法 :return: ''' return '123' def __iter__(self): ''' 将对象变成一个可迭代对象 :return: ''' # return iter('python') yield 1 yield 1 yield 1 yield 1 obj1 = SpecialMember('alex') obj2 = SpecialMember('wusir') print(obj1(1, 2, 3, 4, 5, a=1, b=2)) # __init__ __new__ print(obj2['__getitem__']) # __getitem__ obj1['name'] = 'alex' # __setitem__ print(obj1.dic) del obj1['name'] # __delitem__ print(obj1.dic) print(obj1 + obj2) # __add__ with obj1 as f: # __enter__ __exit__ print(123) print(f) print(obj1) # __str__ print(obj1.__doc__) # __doc__ 不属于类特殊方法 for item in obj1: # __iter__ print(item) print(obj1.__dict__) # __dict__ 获取对象所有实例变量的值