夜阑卧听风吹雨

铁马冰河入梦来

Loading

Python-3 模块 类

模块

  1. 模块名.py(不应使用中文,这里仅做示范)
  2. 如果是放在包里的:ercilan.模块名
  • 这个包是一个目录,目录下需要有标识文件(可为空):__init__.py
    • __init__.py 表示这个目录也是一个模块。
  • 可以有多级目录,但如果是要作为包的都需要有 __init__.py
  • 自己创建的如果与 Python 内部模块重名,则内部模块会被覆盖无法使用。
  • Python 模块的标准文件模板:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'本模块的文档注释' # 任何模块代码的第一个字符串都被视为模块的文档注释;

__author__ = '二次蓝' # 搞了个署名。。

import sys # 通过sys模块可以获取命令行参数

if name == '__main__': # 命令行调用会将__name__设为__main__
    print('使用命令行调用了') # 让一个模块通过命令行运行时执行一些额外的代码,常见于运行测试。
sys.exit() # 你的模块代码
  • sys 模块有一个 argv 列表变量,储存了调用模块时的命令行的所有参数。第一个值是模块的文件名。
    • 比如命令行调用 1.py 模块带 参数1,那么 sys.argv['1.py', '参数1']
  • sys.path 包含了解释器查找模块的所有目录。
  • 写多少个 import test 都只会导入一个 test 模块。

作用域

  • __ 开头结尾的是特殊变量,自己一般不要使用这种命名;
  • _xxx__xxx 这样的函数或变量就是非公开的(private),不应该被直接引用,比如 _abc__abc 等;
  • 因为 Python 并无限制访问权限,所以这种命名是一种约定俗成的习惯,应该遵守。
  • 在类里面会将 __abc 修改为其他名字,间接实现私有。(单下划线没有)

安装第三方模块

pip install Pillow # 安装Pillow模块

更新 pip:python -m pip install --upgrade pip

  • 镜像安装模块:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名

使用 Anaconda

Anaconda 是一个用于科学计算的 Python 发行版,支持 Linux, Mac, Windows, 包含了众多流行的科学计算、数据分析的 Python 包。

anaconda 英 [ˌænəˈkɒndə] 美 [ˌænəˈkɑːndə] n. 水蟒;蟒蛇

  • 可以快捷的获取包并管理,创建不同的环境运行 Python。
  • 包含了conda、Python在内的超过180个科学包及其依赖项。

镜像下载:Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror
推荐阅读:Anaconda介绍、安装及使用教程 - 简书

  • Miniconda 是一个 Anaconda 的轻量级替代,默认只包含了 python 和 conda,但是可以通过 pip 和 conda 来安装所需要的包。

Miniconda 安装包可以到 https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/ 下载。

更新源修改

查看已设置软件源:

conda config --show
# 在显示的配置中可以找到channels,-defaults为预设值

删除指定 url:conda config --remove channels url地址
添加更新源:

# 清华大学开源软件镜像
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r

设置搜索时显示通道地址:

conda config --set show_channel_urls yes

清除索引缓存,保证用的是镜像站提供的索引:conda clean -i

anaconda | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror

避免出现其他错误,在 anaconda prompt 里更新一下所有工具包:

conda upgrade --all

class Student(object): # object为继承的父类,Python里object类是小写诶 
    job = 'student' # 类的属性
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def get_age(self): # 必须要有self
        print(self.age)
  • __init__ 函数相当于 java 中的构造器
  • self 用来表示 java 中的 this,是类的每个函数必需的第一个参数
  • 实例化时只需要且必须要传入 nameage
  • 注意:与静态语言 Java 不同,动态语言 Python 可以对实例对象动态的添加不同的变量。因此一个类的不同实例可能有不同的属性/变量数量。
stu1 = Student('小蓝', 22) # 实例化一个Student对象
stu1.score = 99 # 给这个对象动态添加属性

类变量和成员变量

--> 类属性和实例属性
直接在 class 下面:job = 'student',这个是类的属性;
self.name 是实例属性。

  • 类的属性是所有实例可以修改的,大家共享一个。类似于 Java 的 static
  • 访问类对象使用 Student.job,无论在类内部还是外面,这样好使。
  • 为一个类动态添加方法或属性也这么使用 Student.sing = ...

访问权限限制

将类的变量名前添加 __即可变成私有变量(private)。

# ...
        self.__age = age # 私有变量,访问出现:AttributeError: 'Student' object has no attribute '__age'
        # 实际上实例对象的这个变量被改为了 __Student__age
        # 要是你自己“修改”:stu1.__age = 18
        # 实际上是个这个实例对象添加了属性,而不是修改
#...
  • 注意:变量名前后都有 __ 的是特殊变量,可以访问,不是私有的
  • _ 单下划线开头 Python 不会修改变量名,但是一般我们也不要去直接访问它

多态

假设我们又写了两个子类 BoyStu 和 GirlStu,继承自 Student。

  1. isinstance(BoyStu, Student)True。可以使用 type
  2. 鸭子类型:理解有这个类型就行了。然后知道下面加粗部分。
def run_twice(student):
    student.run()
    student.run()

Java 中,调用方法需要传入对象需要为其父类或子类。而动态类型 Python 不同,只要有 run() 函数,就能运行。

  • 动态语言的“鸭子类型”:一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
  • 其实也就是动态语言没有强制要求函数的参数类型。有这个方法,就能运行起来。

获取对象信息

  1. 查询判断变量类型:
value_type = type(o)
# 然后可与以下内置变量对比
types.FunctionType
types.BuiltinFunctionType
types.LambdaType
types.GeneratorType

判断是不是这些类型的其中一种:

# 判断lista是不是其中的一种类型
isinstance(lista, (list, tuple))
  1. 获取对象的所有属性和方法:dir(o)
  2. getattr(o, 'x', 404):获取对象 o 的 x 属性/方法,不存在返回值:404
    hasattr(o, 'x'):判断对象 o 是否有 x 属性/方法
    setattr(o, 'x', 18):给对象 o 设置 x 属性为 18(当然也可以设置为方法,变量可以指向函数嘛)

类:限制设置属性

限制 Student 类,只能绑定 name 和 age 属性:

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
  • 注意:子类不会默认继承这个限制,就会无限制。-
  • 在子类继续使用 __slots__,那么子类的限制绑定就是父类的 __slot__ 加上自己的 ___slot__
  • 超出绑定:AttributeError: 'Student' object has no attribute 'height'

@property 装饰器

可以将方法返回的值变成属性,方法名作为属性名;在这个方法里设置好对属性的检查等。

  1. @property 放在一个方法前,方法的名字表示属性的名字。这个方式是该属性的 getter,返回一个值即可。
# ...
@property
def width(self):
    return self._width
# ...
  • 这里返回了 self._width,那么就需要在 setter 里设置这个值
  • 这里写的代码是通过中间值 _width 来储存的属性
  1. 接下来是 setter 的设置,不设置这个的属性是一个可读属性。
@width.setter
def width(self, value): # value是新值
    # if ... 等检查值的合法性,raise错误
    self._width = value # 最后符合条件的设置值
  1. 完成,外部可以正常操作对象的属性 width:
# 先调用setter设置属性值
o.width = 11
# 再调用getter获取属性值
print(o.width)

虽然从理论上,self._width 的赋值是在 setter 里的,且外部也是这样的调用顺序。但是因为装饰器要生成 @width.setter,所以是写在后头的。

类:多重继承

在类的设计时,优先考虑类的多重继承组合功能,而不是设计多层次复杂的单继承。
拓扑、C3算法

Python多重继承排序原理(拓扑、C3) - 简书

  • 找的规则:没有被继承的(刚开始就是子类本身)
  • 去掉这个类的所有联系,再找上一个(去掉的类括号内继承的父类里调)
  • 括号里有多个,看他们定义位置,写在前面的优先继承;有被其他的继承了的类不能找。
class A(object):
    def foo(self):
        print('A foo')
    def bar(self):
        print('A bar')

class B(object):
    def foo(self):
        print('B foo')
    def bar(self):
        print('B bar')

class C1(A):
    pass

class C2(B):
    def bar(self):
        print('C2-bar')

class D(C1,C2):
    pass

if __name__ == '__main__':
    print(D.__mro__)
    d = D()
    d.foo()
    d.bar()

(标红表示确定了顺序,需要清空它的联系,划掉它
D,继承自 C1、C2,这里面找;
C1 写在前面,且 C1 没有再被继承,C2 等待;
C1 括号里为 A,且 A 没有再被继承;
A 继承自 object,object 还被 B 继承了,不能找,到尽头了,切到等待的 C2;
C2 括号里为 B,且 B 没有再被继承;
B 括号里为 object,且没有再被继承;
。。。还是画图吧

(<class '__main__.D'>, <class '__main__.C1'>, <class '__main__.A'>, <class '__main__.C2'>, <class '__main__.B'>, <class 'object'>)
A foo
A bar

类:定制类

  1. 打印:__str____repr__
# ...
def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__
# ...
  • __str__ 是 Python 调用的 str() 函数。
  • __repr__ 是命令行模式下,敲一个变量直接回车显示的函数。
  • 相当于 Java 的 toString() 方法。

  1. 使得一个类可迭代:__iter____next__raise StopIteration()
class Fib(object): # 斐波那契数列
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值

  1. 按下标可取出:__getitem__()
class Fib(object):
# 上面的代码中添加:
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n): # 返回 range(0, n-1)
            a, b = b, a + b
        return a
  • 支持切片功能的按下标取出:__getitem__()
class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
  • slice 切片类型,有 startstop 属性。
  • 除此之外,需要实现与 Python 内置切片相符合的功能,还要做检查是否为负数,间隔取值等等。
  • __setitem__()方法,把对象视作list或dict来对集合赋值。
    __delitem__()方法,用于删除某个元素。

  1. __getattr__:当调用实例对象的属性未找到时,Python 会调用这个方法。
  • 可以把一个类的所有属性和方法调用全部动态化处理
  • 链式调用,【然后干啥?不太懂什么意思】
class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__

试试:

>>> Chain().status.user.timeline.list
'/status/user/timeline/list'
  • 这样,无论 API 怎么变,SDK 都可以根据 URL 实现完全动态的调用,而且,不随 API 的增加而改变。???等到时候实际应用再理解吧

  1. 将实例对象作为函数对象:__call__(这个对象可以 () 运行)
class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print(f'我的名字是{self.name}.')

s1 = Student('小蓝')() # 我的名字是小蓝
s2 = Student('企鹅')
s2() # 我的名字是企鹅
  • 变成了 Callable 对象
  • 查看对象是否可被调用:callable(Student())

类:枚举类

  1. 创建枚举类
from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
  • Enum('类名', ('对象名1', '对象名2'))
  • 它的值是按顺序从数字 1 开始增加
  • Month.__members__ 是储存了所有对象的有序字典 OrderedDict,遍历:
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)
  • 需要自定义值的时候就需要自定义类:
from enum import Enum, unique

@unique # 确保值唯一
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
  • 作用:每个常量都是 class 的一个不可更改的唯一实例
  1. 访问枚举类:
Month.Jan # Month.Jan
Month['Jan'] # Month.Jan
Month(1) # Month.Jan 注意是从1开始的
# 访问值
Month.Jan.value # 1

类:元类

动态创建类

在 Java 中,类的类型是 class,而 Python 中类的类型是 type
实例对象的类型是 ***类。

# Hello是一个类
>>> print(type(Hello))
<class 'type'>
#h是Hello类的实例
>>> print(type(h))
<class 'hello.Hello'>

type() 还可以创建类!跟 class Hello(object): 的功能一样。

def fn(self, name='world'): # 首先要有一个类的方法,创建类的时候使用
    print('hello', name)

Hello = type('Hello', (object,), dict(greet=fn)) # 创建Hello class
  • 第一个参数是类的名字
  • 第二个参数是继承的父类的元组(记得元组单个元素时的写法)
  • 第三个参数是设置属性/方法的字典
  • 实际上 class 定义类底层也是调用的 type() 方法。

metaclass

元类,就是说,通过元类创建类,再通过类创建实例对象。
metaclass 是 Python 面向对象里最难理解,也是最难使用的魔术代码。基本上很少用到。

  • 元类命名通常以 Metaclass 结尾。
class ListMetaclass(type): # 需要继承自type类
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

TO BE CONTINUED

posted @ 2020-12-04 21:35  二次蓝  阅读(137)  评论(0编辑  收藏  举报