面向对象(二)
类的成员
类的成员可以分为三大类:字段、方法和属性
一、字段
字段包括普通字段和静态字段,他们在定义和使用中有所区别,最本质的区别是内存中保存的位置不同。
- 普通字段属于对象,在内存中为每个对象保存一份
- 静态字段属于类,在内存中只保存一份
一般情况下,自己访问自己字段,普通字段只能使用对象访问,静态字段使用类访问(最好不要用对象访问静态字段),静态字段在代码加载时创建。
#!/usr/bin/env python # coding=utf-8 class Province(object): # 静态字段,保存在类中 country = '中国' def __init__(self, province): # 普通字段,保存在对象中 self.name = 'province' # 普通方法,由对象去调用执行(方法属于类) def show(self): return self.name # 静态方法,由类调用执行,对象是封装数据的,用不到对象封装数据,使用静态方法 @staticmethod def f1(): return '....' test = Province('henan') print(Province.country) print(test.country) print(test.show()) print(Province.f1())
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
二、方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
#!/usr/bin/env python # coding=utf-8 # 面向对象中类成员 # 字段: 静态字段, 普通字段 # 方法: 普通方法 静态方法, 类方法 # 所有方法属于类 # 普通方法:至少一个self,对象执行 # 静态方法:任意参数, 类执行(对象执行,基本不要用) # 类方法: 至少一个cls,类执行(对象执行,基本不要用) # 属性: 看pager.py (@property) # 具有方法的写作形式,具有字段的访问形式 class Province(object): # 静态字段,保存在类中 country = '中国' def __init__(self, province): # 普通字段,保存在对象中 self.name = 'province' # 普通方法,由对象去调用执行(方法属于类) def show(self): return self.name # 静态方法,由类调用执行,对象是封装数据的,用不到对象封装数据,使用静态方法 # 可以有参数,可以没有 @staticmethod def f1(): return '....' # 类方法 # 必须有一个参数cls, cls是类名 加()创建对象 @classmethod def f2(cls): print(cls.country) print(cls) test = Province('henan') print(Province.country) print(test.country) print(test.show()) print(Province.f1()) Province.f2()
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
三、属性
属性定义有两种方式
- 装饰器 在方法上应用装饰器
#!/usr/bin/env python # coding=utf-8 class Pager(object): def __init__(self, page): self.all_count = page # 获取 @property def all_pager(self): a1, a2 = divmod(self.all_count, 10) if a2 == 0: return a1 else: return a1 + 1 # 设置 @all_pager.setter def all_pager(self, value): print(value) # 删除 @all_pager.deleter def all_pager(self): print('del all_pager') p = Pager(101) ret = p.all_pager # 自动去执行Pager里面的@property下的all_pager方法,下同 print(ret) p.all_pager = 111 del p.all_pager
- 静态字段 在类中定义为property对象的静态字段
1 #!/usr/bin/env python 2 # coding=utf-8 3 class Pager: 4 def __init__ (self, all_count): 5 self.all_count = all_count 6 def f1(self): 7 return 123 8 def f2(self, value): 9 pass 10 def f3(self): 11 pass 12 foo = property(fget=f1, fset=f2, fdel=f3) 13 p = Pager(101) 14 ret = p.foo # 执行fget 15 print(ret) 16 p.foo = 'hexm' # 执行fset 17 del p.foo # 执行fdel
类成员修饰符
对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法
1 class Foo(object): 2 __cc = '123' # 公有静态字段 3 4 def __init__(self, name): 5 # 私有的,只能在类内部访问 6 self.__name = name # 私有字段 7 8 def f1(self): 9 print(self.__name) 10 11 def f2(self): 12 print(Foo.__cc) 13 14 obj = Foo('hexm') 15 obj.f1() 16 # print(obj.__name) # 错误 17 print(obj._Foo__name) # 最好不要用这种方式访问私有字段 18 19 obj.f2() 20 print(Foo.f1__cc) 21 22 # 继承Foo也不能访问私有的 23 class Bar(Foo): 24 def f2(self): 25 print(self.__name) 26 27 obj = Bar('hexm') 28 obj.f1() 29 obj.f2() # 错误
类的特殊成员
上面介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:
1. __doc__
表示类的描述信息
1 class Foo: 2 """ 描述类信息 """ 3 4 def func(self): 5 pass 6 7 print Foo.__doc__
2. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 class C(object): 5 6 def __init__(self): 7 self.name = 'hexm'
1 from lib.aa import C 2 3 obj = C() 4 print obj.__module__ # 输出 lib.aa,即:输出模块 5 print obj.__class__ # 输出 lib.aa.C,即:输出类
3. __init__
构造方法,通过类创建对象时,自动触发执行。
4. __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
5.__call__
对象后面加括号,触发执行
1 class Foo(object): 2 def __init__(self): 3 # 构造方法,创建对象自动执行 4 print('init') 5 def __del__(self): 6 # 析构方法 7 pass 8 def __call__(self): 9 print('call') 10 p = Foo() 11 ret = p.__class__ 12 print(ret) 13 p() # 对象后面加括号,执行__call__方法 14 Foo()() # 先执行构造方法再执行call
6.__dict__
类或对象里面所有成员
1 class Foo: 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 def __call__(self): 6 return 123 7 test1 = Foo('hexm', 18) 8 test2 = Foo('zhuxj', 17) 9 # 对象里面封装的字段 10 ret = test1.__dict__ 11 print(ret) 12 # 类里面所有成员 13 print(Foo.__dict__)
7.__str__
在类中定义了__str__方法,在打印对象时,默认输出该方法的返回值
1 #!/usr/bin/env python 2 # coding=utf-8 3 class Foo: 4 def __init__(self, name, age): 5 self.name = name 6 self.age = age 7 def __str__(self): 8 # print 对象时,自动执行该方法 9 return '%s---%s' % (self.name, self.age) 10 test1 = Foo('hexm', 18) 11 test2 = Foo('zhuxj', 17) 12 print(test1) 13 print(test2) 14 ret1 = str(test1) 15 ret2 = str(test2) 16 print(ret1) 17 print(ret2)
8.__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
1 #!/usr/bin/env python 2 # coding=utf-8 3 class Foo: 4 def __init__(self, name, age): 5 self.name = name 6 self.age = age 7 def __str__(self): 8 # print 对象时,自动执行该方法 9 return '%s---%s' % (self.name, self.age) 10 def __getitem__(self, item): 11 if type(item) == slice: 12 print(item.start) 13 print(item.stop) 14 print(item.step) 15 elif type(item) == str: 16 print(item) 17 def __setitem__(self, key, value): 18 # print(type(key), type(value)) 19 if type(key) == slice: 20 print(key,value) 21 def __delitem__(self, key): 22 print(type(key)) 23 test1 = Foo('hexm', 18) 24 test1['sb'] # 执行__getitem__ 字符串类型 25 test1[1:3:1] # 执行__getitem__ slice类型 26 test1['k1'] = 'hexm' # 执行 __setitem__ 27 test1[1:4] = [1, 2, 3, 4] 28 del test1['k1'] # 执行 __delitem__ 29 del test1[1:4]
9.__iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了__iter__
1 #!/usr/bin/env python 2 # coding=utf-8 3 class Foo: 4 def __iter__(self): 5 return iter([1, 2, 3, 4]) 6 test = Foo() 7 # 把对象放到for循环中,会自动执行__iter__方法, 返回的值得可迭代对象 8 for item in test: 9 print(item)
10. __add__, __del__
在类中定义__add__, __del__时,执行两个对象相加,执行__add__,两个对象相减,执行__del__
1 def __add__(self, lover): 2 xxx
11. issbuclass, isinstance
1 #!/usr/bin/env python 2 # coding=utf-8 3 class Foo: 4 def __iter__(self): 5 return iter([1, 2, 3, 4]) 6 7 class Fo(Foo): 8 pass 9 test = Foo() 10 # 对象是否属于某个类 11 ret = isinstance(test, Foo) 12 print(ret) 13 ret = isinstance(test, Fo) 14 print(ret) 15 # 一个类是否是另一个类的子类 16 ret = issubclass(Fo, Foo) 17 print(ret)
12.__enter__
当python使用with-as语法,就会调用__enter__函数,然后把函数的return值传给as后指定的变量。然后执行with-as下的语句,无论出现了什么异常,都会在离开时执行__exit__。
为了让一个对象兼容with语句,需要实现__enter__()和__exit__()方法。
1 from socket import socket, AF_INET, SOCK_STREAM 2 class LazyConnection: 3 def __init__(self, address, family=AF_INET, type=SOCK_STREAM): 4 self.address = address 5 self.family = family 6 self.type = type 7 self.connections = [] 8 def __enter__(self): 9 sock = socket(self.family, self.type) 10 sock.connect(self.address) 11 self.connections.append(sock) 12 return sock 13 def __exit__(self, exc_ty, exc_val, tb): 14 self.connections.pop().close() 15 # Example use 16 from functools import partial 17 conn = LazyConnection(('www.python.org', 80)) 18 with conn as s1: 19 pass 20 with conn as s2: 21 pass 22 # s1 and s2 are independent sockets
创建大量对象时节省内存方法
如果程序要创建(上百万)大量对象,导致占用很大内存。可以给类添加__slots__属性来极大减少实例所占的内存。使用slots后不能给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
比如:
#!/usr/bin/env python # coding=utf-8 class Date(objct): __slot__ = ['year', 'month', 'day'] def __init__(self, year, month, day): self.year = year self.month = month self.day = day