类与对象

类与对象


  1"""
  2Author: wangy325
  3Date: 2024-07-18 16:28:29
  4Description: 类
  51) 作用域和命名空间 scope.py
  62) Class对象
  73) 实例对象
  84) 方法对象
  95) 类变量与实例变量
 106) 继承
 117) 多继承
 128) 变量要"私有"
 139) 迭代器与生成器
 14"""
 15
 16
 17class EmptyError(BaseException):
 18    pass
 19
 20
 21class MyClass:
 22    # 类变量 (Java静态域)
 23    property = 'MyClass\'s property'
 24    trick = []
 25
 26    # 构造器
 27    def __init__(self, *args) -> None:
 28        # 实例变量(Java私有域)
 29        self.data = []
 30        if args is not None and len(args) > 0:
 31            self.data.append(args)
 32        self.size = len(self.data)
 33
 34    # 它既是'函数', 也是'方法对象'
 35    def add(self, e):
 36        self.data.append(e)
 37
 38    def remove(self):
 39        if self.size > 0:
 40            self.data.pop()
 41            self.size = self.size - 1
 42        else:
 43            raise EmptyError('data is empty.')
 44
 45    def fun(self):
 46        # 局部变量
 47        property = 'Let\'s hava fun!'
 48
 49
 50# type(MyClass)
 51print(MyClass.__class__)  # <class 'type'>
 52
 53# 实例对象
 54cls, cls2 = MyClass(), MyClass()
 55print(f'{type(cls)}')
 56
 57# 类变量可以在模块作用域直接读取, 并且修改
 58print(MyClass.property)
 59MyClass.property = f'property changed by {MyClass.__class__}'
 60print(f'MyClass.property: {MyClass.property}')
 61MyClass.trick.append('lie')
 62
 63# 实例对象也可以读取并修改类变量
 64cls.property = f'property changed by {cls}'
 65print(f'cls.property: {cls.property}')
 66cls.trick.append('truth')
 67print(f'cls.trick: {cls.trick}')
 68# 注意看, cls2的property没随cls变
 69# 但是 trick随cls变了
 70# 这是因为, str 是'不可变常量'
 71# 而list是引用对象
 72print(f'cls2.property: {cls2.property}')
 73print(f'cls2.trick: {cls2.trick}')
 74
 75# 尽管实例的修改仅仅对自己有效,
 76# 但是要改动类变量是非常轻松的
 77# 所以, python中的类变量是非常'不安全'的
 78
 79# 实例变量是隐藏在构造器中的, MyClass '看不到'它们
 80try:
 81    print(MyClass.data)
 82except Exception as e:
 83    print(f'ERROR: {e}')
 84# 但是实例对象可以读取, 并修改
 85cls.data.append('apple')
 86print(f'All cls\'s attributes: {cls.data}, {cls.size}')
 87# 同样地, 仅仅对自身有效
 88print(f'All cls2\'s attributes: {cls2.data}, {cls2.size}')
 89
 90# 除了类变量, 模块作用域里还可以访问类里面的函数
 91f = MyClass.fun
 92ff = cls.fun()
 93print(f'{f == ff}: {type(f)}{type(ff)}')
 94# 显然, 上面2个是不一样的
 95# 通过类名获取的是一个函数
 96# 通过实例获取的是一个方法引用
 97print(ff == MyClass.fun(cls))  # true
 98
 99
100# 需要说明的是, 通过实例调用的'方法',
101# 都会隐式地将该实例作为参数, 传递给方法
102# 因此, 类的方法总有一个参数: self
103
104
105# 名称改写
106# 由于python没有访问权限修饰符, 所有的变量都是`public`的
107# 为了保护'私有'变量
108# 约定将以'__'开头的变量和函数认定为私有的
109# 可以使用setter/getter方法来访问属性
110class Mapping:
111
112    def __init__(self, **args):
113        self.__dict = {}
114        self.__update(**args)
115
116    def __update(self, **kwargs):
117        self.update(**kwargs)
118
119    def update(self, **args):
120        if args is not None and len(args.keys()) > 0:
121            for k, v in args.items():
122                self.__dict[k] = v
123            # self.__dict = {k: v for k in args.keys() for v in args.values()}
124
125    def print_map(self):
126        print(self.__dict)
127
128
129mapp = Mapping(name='anna', age=18)
130mapp.print_map()
131# 名称改写, 将__dict隐藏起来了
132try:
133    print(mapp.__dict)
134except Exception as er:
135    print(er)  # 'Mapping' object has no attribute '__dict'
136# 不过, 还是可以通过 _Class__attribute的方式访问
137print(mapp._Mapping__dict)
138
139
140# 方法同理
141
142# ################# #
143#    Python的继承    #
144# ################# #
145
146
147class BaseClass:
148    base_class_attr = 'Base attr'
149
150    def __init__(self, arg: str):
151        print('BaseClass with-args init')
152        self.base_instance_attr = arg
153        self.__base_hidden_attr = arg
154
155    def __hidden_show(self):
156        print(f'base_class_attr: {BaseClass.base_class_attr}\n'
157              f'base_instance_attr: {self.base_instance_attr}\n'
158              f'__base_hidden_attr: {self.__base_hidden_attr}')
159
160    def show(self):
161        self.__hidden_show()
162
163
164class SubClass(BaseClass):
165    pass
166
167
168# 子类可以访问父类的类变量
169print(SubClass.base_class_attr)
170
171# 子类使用调用父类的有参构造器
172# Java是不行的
173subclass = SubClass('sub_class_attr')
174subclass.show()
175
176# ###################### #
177#       对象迭代器         #
178# ###################### #
179'''
180迭代器(iterator)
181只要是实现了 __iter__()和__next__()方法的类,
182都可以实现迭代器的行为.
183
184迭代器什么行为? 可以使用for...in
185比如list, 可以用 for e in [1, 2, 3]
186比如str, for c in 'str'
187比如file, for line in open('file.txt')
188
189在幕后, for 语句会在容器对象上调用 iter()。 
190该函数返回一个定义了 __next__() 方法的迭代器对象,
191此方法将逐一访问容器中的元素。 
192当元素用尽时, __next__() 将引发 StopIteration 
193异常来通知终止 for 循环。 
194你可以使用 next() 内置函数来调用 __next__() 方法
195
196自定义类也是如此, 可以为任何类添加迭代器
197'''
198
199
200class Iter_class():
201
202    def __init__(self, *args) -> None:
203        self.__data = list(args)
204        self.__index = len(self.__data)
205
206    def __iter__(self):
207        return self
208
209    def __next__(self):
210        if self.__index == 0:
211            raise StopIteration
212        self.__index = self.__index - 1
213        return self.__data[self.__index]
214
215    # 非必需方法
216    def has_next(self):
217        return self.__index > 0
218
219
220it = Iter_class(1, 2, 3)
221while it.has_next():
222    print(next(it), end=', ')
223for e in it:
224    print(e, end='$ ')
225
226# ############ #
227#   生成器      #
228# ############ #
229'''
230生成器用于快速创建迭代器
231
232它会自动创建__iter__()和__next__()方法
233'''
234
235
236# 以下函数创建了一个生成器
237def reverse(data):
238    for index in range(len(data) - 1, -1, -1):
239        yield data[index]
240
241
242for c in reverse('golf'):
243    print(c, end=',')
244
245# 当然, str本身就支持迭代器, 上述栗子只是为了阐述写法
246# 生成器还可以使用生成器表达式
247# 生成器表达式使用()而不是[], 后者是列表推导式!
248print()
249print(list(i for i in 'golf'))
250print(sum(i * i for i in range(10)))
251vx = [1, 2, 3]
252vy = [6, 7, 8]
253print(list(x * y for x, y in zip(vx, vy)))
254# 生成器表达式比较简单, 一般不用来出来复杂的逻辑
255# 生成器表达式相比等效的列表推导式, 更加节省内存
256l = list(e for e in range(5))
257# 等效于
258l2 = [e for e in range(5)]
259'''
260>>> (i for i in range(3))
261<generator object <genexpr> at 0x10463ba00>
262>>> [i for i in range(3)]
263[0, 1, 2]
264>>> {x:x*x for x in range(1,4,1)}
265{1: 1, 2: 4, 3: 9}
266'''
267
268# 关于yield表达式 #
269# 在生成器中使用了yield表达式,yield表达式也只在生成器中使用
270# https://docs.python.org/zh-cn/3/reference/expressions.html#yield-expressions