From ab6898d7ebb53c9bfe5d7009556486b1bd8bce66 Mon Sep 17 00:00:00 2001 From: asahi Date: Mon, 18 Aug 2025 23:57:01 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E9=98=85=E8=AF=BBpython=E9=9D=A2?= =?UTF-8?q?=E5=90=91=E5=AF=B9=E8=B1=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/py.md | 344 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) diff --git a/python/py.md b/python/py.md index 36bd6af..bf283b8 100644 --- a/python/py.md +++ b/python/py.md @@ -103,6 +103,23 @@ - [global](#global) - [nonlocal](#nonlocal) - [global namespace](#global-namespace) + - [Class Objects](#class-objects) + - [__init__](#init) + - [data attributes](#data-attributes) + - [method object](#method-object) + - [class and instance variables](#class-and-instance-variables) + - [function object](#function-object) + - [Inheritance](#inheritance) + - [attribute resolving](#attribute-resolving) + - [method resolving](#method-resolving) + - [built-in functions](#built-in-functions) + - [private variables](#private-variables) + - [name mangling](#name-mangling) + - [odds and ends](#odds-and-ends) + - [`__self__, __func__`](#__self__--__func__) + - [iterators](#iterators) + - [generators](#generators) + - [generator expression](#generator-expression) # Python @@ -1691,3 +1708,330 @@ After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam ``` + +### Class Objects +class对象支持两种类型的操作,`attribute reference`和`instantiation`。 + +attribute reference使用`obj.name`的语法。class定义如下: +```py +class MyClass: + """A simple example class""" + i = 12345 + + def f(self): + return 'hello world' +``` +`MyClass.i`和`MyClass.f`是有效的attribute reference,会返回一个integer和一个function。 + +`__doc__`也是一个有效的attribute,返回对该class的简单文档描述。 + +类实例化类似于函数的调用,其语法如下: +```py +x = MyClass() +``` +上述示例会创建一个MyClass类型的对象,并且将其赋值给本地变量x。 + +#### __init__ +python中支持在初始化时为对象指定状态,故而可以自定义构造器,示例如下: +```py +class Complex: + def __init__(self, realpart, imagpart): + self.r = realpart + self.i = imagpart + +c = Complex(3.0, -4.5) +``` +#### data attributes +python中的data attributes类似于c++中的data members,但是,`data attributes`并不需要被声明,其类似于local variables,其在第一次赋值时就存在了。 + +例如,`变量x是MyClass类的实例`,那么如下代码将会输出`16`. + +#### method object +除了通过data attribute外,另一种attribute reference的方式是method。 + +在python中,class中的function定义了object中的method。但是,`x.f`和`MyClass.f`并不等价,前者是method object,后者是function object。 + +通常,method在其所绑定的对象后调用: +```py +x.f() +``` + +python中,`x.f`为一个method object,支持被存储在变量中,并可以在后续通过变量调用,示例如下: +```py +xf = x.f + +while True: + print(xf()) +``` +如上所示,method object `x.f`被存储在local variable `xf`中后,后续可以通过`xf()`来调用method object。 + +> 按照`MyClass`的类定义,`x.f`方法将会接收一个`self`参数,但是在调用`xf()`时,并没有传递任何参数。 + +实际上,对于method而言,其method所绑定的对象实例将会作为参数被传向function的第一个参数`self`。 + +故而,对于method object和function object而言,`x.f()`实际上等价于`MyClass.f(x)`。 + +即,对于method object而言,`obj.method(arg1, arg2 ...)`的调用等价于`Class.method(obj, arg1, arg2 ...)`的function调用。 + +### class and instance variables +简单来说,每个对象的instance variables是相互独立的,但是class variables则是在同一类的所有对象间都是共享的。 + +```py +class Dog: + + kind = 'canine' # class variable shared by all instances + + def __init__(self, name): + self.name = name # instance variable unique to each instance + +>>> d = Dog('Fido') +>>> e = Dog('Buddy') +>>> d.kind # shared by all dogs +'canine' +>>> e.kind # shared by all dogs +'canine' +>>> d.name # unique to d +'Fido' +>>> e.name # unique to e +'Buddy' +``` + +对于class variables而言,其既可以通过`MyClass.name`来进行访问,也可以通过`obj.name`来进行访问。 + +但是,在class variable和instance variable同名时,instance variable会优先被访问,示例如下: +```py +class Warehouse: + purpose = 'storage' + region = 'west' + + w1 = Warehouse() +print(w1.purpose, w1.region) +storage west +w2 = Warehouse() +w2.region = 'east' +print(w2.purpose, w2.region) +storage east +``` + +在python中,用户既可以通过method来访问data attributes,也可以通过对象直接访问。并且,python中也没有类似其他语言中`private`这类强制隐藏的机制,`python中都是基于约定`。 + +在使用data attributes时,应当小心如下场景: +- data attributes可能被method维护和管理,若用户不经过method直接修改attribute的值,可能会造成attribute的状态被破坏(没有经过method中的相关校验直接将attribute改为错误值) +- 用户可以在`避免命名冲突`的前提下向data attributes添加自己的值 + +在python中,对象中method的第一个参数名为`self`,这只是一个约定,`self`在python中并没有特别的意义。 + +#### function object +python中,function object为一个class attribute,用于定义method。function object并不一定要定义在class内部,示例如下: +```py +# Function defined outside the class +def f1(self, x, y): + return min(x, x+y) + +class C: + f = f1 + + def g(self): + return 'hello world' + + h = g +``` +在上述示例中,类`C`包含`f, g, h`三个attributes,C实例中也包含`f, g, h`三个method。其中,`h`和`g`完全等价。 + +在python的class定义中,method可以通过`self.xxx`调用其他method,示例如下: +```py +class Bag: + def __init__(self): + self.data = [] + + def add(self, x): + self.data.append(x) + + def addtwice(self, x): + self.add(x) + self.add(x) +``` +method中同样可以访问global names,method关联的global scope是method定义所位于的module。 + +> 在python中,每个值都是一个对象,其class信息存储在`object.__class__`中。 + +### Inheritance +在python中,支持类的继承,示例如下: +```py +class DerivedClassName(BaseClassName): + + . + . + . + +``` + +#### attribute resolving +若派生类B继承了基类A,那么在对B的对象进行attribute解析时,其首先会查找派生类,如果在派生类中没有找到,则会继续在基类中查找。 + +#### method resolving +method解析也会从派生类开始,沿着基类逐渐查询,直到找到对应的function object。 + +在python中,派生类的方法会覆盖基类的方法,用c++中的概念来理解,即python中所有的method都是virtual的。 + +在python中,如果派生类方法中想要调用基类的方法,可以通过`BaseClassName.methodname(self, arguments)`,其类似于其他语言中的`super`。 + +#### built-in functions +python中包含如下built-in function来判断继承关系: +- `isinstance`: 该方法通常用于检查是否实例属于某一个类 + - `isinstance(obj, int)` +- `issubclass`: 该方法通常用于检查是否一个类派生自另一个类 + - `issubclass(bool, int)`: 该调用返回为True,bool是int的子类 + +### private variables +python中并不存在只能从类内部访问的`private instance variable`。但是,python编码中存在一个规范约束: +- 以`_`开头的名称应当被作为api中的非公开部分(下划线开头的可以是function, method, data member) + +#### name mangling +在类定义中,任何以`__spam`定义的name(至少两个前缀下划线,最多一个后缀下划线),都会被替换为`_classname__spam`的形式。 + +name magling在不破坏列内部调用的场景下十分有用,示例如下: +```py +class Mapping: + def __init__(self, iterable): + self.items_list = [] + self.__update(iterable) + + def update(self, iterable): + for item in iterable: + self.items_list.append(item) + + __update = update # private copy of original update() method + +class MappingSubclass(Mapping): + + def update(self, keys, values): + # provides new signature for update() + # but does not break __init__() + for item in zip(keys, values): + self.items_list.append(item) +``` +在上述代码中,即使在`MappingSubclass`中引入`__update`,其名称也为`_MappingSubclass__update`,和`_Mapping__update`不同,仍然不会对父类造成影响。 + +在类中以`__spam`定义的变量,在类外部可以以`_classname__spam`进行访问。 + +### odds and ends +有时想使用类似C语言中的struct,惯用方法是使用`dataclasses`,示例如下所示: +```py +from dataclasses import dataclass + +@dataclass + class Employee: + name: str + dept: str + salary: int +``` +```py +>>> john = Employee('john', 'computer lab', 1000) +>>> john.dept +'computer lab' +>>> john.salary +1000 +``` + +### `__self__, __func__` +instance method object拥有如下属性: +- `__self__`: `m.__self__`代表method m关联的实例对象 +- `__func__`: `m.__func__`代表method关联的function object + +### iterators +在python中,大多数容器对象都可以通过for-statements进行迭代: +```py +for element in [1, 2, 3]: + print(element) +for element in (1, 2, 3): + print(element) +for key in {'one':1, 'two':2}: + print(key) +for char in "123": + print(char) +for line in open("myfile.txt"): + print(line, end='') +``` +在底层,for-statement针对容器对象调用了`iter()`方法,该方法会返回一个`iterator`对象,iterator对象中定义了`__next__()`方法,该方法在终止时会抛出`StopIteration`异常。 + +可以通过内置的`next()`方法来调用`__next__()`,示例如下所示: +```py +>>> s = 'abc' +>>> it = iter(s) +>>> it + +>>> next(it) +'a' +>>> next(it) +'b' +>>> next(it) +'c' +>>> next(it) +Traceback (most recent call last): + File "", line 1, in + next(it) + StopIteration +``` +如果想要为自定义类添加iterator,可以在类中定义一个`__iter__()`方法,该方法返回一个带`__next__()`方法的对象。`如果一个class中包含了__next__()方法的定义,那么__iter__()方法仅需返回self即可`。 + +```py +class Reverse: + """Iterator for looping over a sequence backwards.""" + def __init__(self, data): + self.data = data + self.index = len(data) + + def __iter__(self): + return self + + def __next__(self): + if self.index == 0: + raise StopIteration + self.index = self.index - 1 + return self.data[self.index] +``` + +### generators +`Generators`可以简单的创建iterator,其编写和函数类似,但是当想要返回值时,使用`yield statement`。 + +每次调用`next()`时,generator都会从上次中止的地方进行恢复。示例如下所示: +```py +def reverse(data): + for index in range(len(data)-1, -1, -1): + yield data[index] +``` +```py +>>> for char in reverse('golf'): + print(char) + +f +l +o +g +``` +通过generator完成的任何事情都可以通过iterator来完成,但是generator的代码更加紧凑,其`__iter__`和`__next__`方法都是自动生成的。 + +在generator的多次next调用之间,local variables的状态和执行状态也是自动保存的。 + +generator令iterator的编写更加简单。 + +### generator expression +一些简单的迭代器可以简化为表达式: +```py +>>> sum(i*i for i in range(10)) # sum of squares +285 + +>>> xvec = [10, 20, 30] +>>> yvec = [7, 5, 3] +>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product +260 + +>>> unique_words = set(word for line in page >>> for word in line.split()) + +>>> valedictorian = max((student.gpa, student.name) for student in graduates) + +>>> data = 'golf' +>>> list(data[i] for i in range(len(data)-1, -1, -1)) +['f', 'l', 'o', 'g'] +```