Python基础语法

◀▶ Python 变量命名规范

★ 类属性命名规范

# 类属性通常采用大写字母、下划线分隔的方式命名,遵循以下规范:
1. 如果类属性是常量,通常使用全大写的字母表示,多个单词之间用下划线分隔,例如:MAX_SIZE。
2. 如果类属性表示一个布尔值或状态,通常使用is或has开头,例如:is_running、has_finished。
3. 如果类属性表示一个私有属性,通常在属性名前加上一个下划线,例如:_count。
4. 如果类属性表示一个类方法或静态方法,通常使用驼峰式命名法,例如:getMaxSize()。
5. 如果类属性表示一个类变量或实例变量,通常使用小写字母、下划线分隔的方式命名,例如:instance_count。

★ 私有化命名规范

命名格式 说明
xx 公有变量; 没有任何前缀的变量被视为"公有"的变量. 在 Python 中,并没有像其他编程语言那样显式定义"公有变量"。Python 中的变量默认是可见的
_x 单前置下划线,使用"from somemodule import *" 的方式从一个包导入所有资源, _x不会被导入 但通过 "from somemodule import _x"的方式是可以导入的; 对象属性名以单下划线开头,表示该属性是“受保护的”,通常,这种命名方式用于防止属性被外部直接访问;这不是语法上的要求,而是一种编程的约定,所以实质上依然可以通过 对象._x的方式访问该属性;一般 在使用IDE软件编写代码是不会提示该属性
__xx 双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(本质是名字重整为"_类名__xx"所以访问不到); 一般用于定义类的私有属性
__xx__ 双前后下划线,用户名字空间的魔法对象或属性
xx_ 单后置下划线,用于避免与Python关键词的冲突

◀▶ 环境变量

  • __name__
    模块是对象,并且每个模块都有一个内置属性__name__。
    当一个模块被直接运行的时候,该模块__name__的值就等于缺省的'__main__'。
    如果一个模块被import ,那么这个被引入模块__name__的值就等于该模块名,也就是文件名去掉py扩展名的部分。
    也就是说__name__的值表明了当前py文件调用的方式,因此可以用if __name__ == '__main__'来判断是否是在直接运行该.py文件。 
    
  • sys.path
    当我们导入一个模块时:import  xxx,默认情况下python解析器会搜索当前目录、已安装的内置模块和第三方模块,搜索路径存放在sys模块的path中(sys.path 返回的是一个列表! ):
    当我们要添加自己的搜索目录时,可以通过列表的append()方法;
    对于模块和自己写的脚本不在同一个目录下,在脚本开头加sys.path.append('模块地址')或sys.path.insert(0,'模块地址')
    

◀▶ 正则表达式

★ 字符匹配

  • 单字符匹配
    字符 功能
    . 匹配任意1个字符(除了\n)
    [ ] 匹配[ ]中列举的字符
    \d 匹配数字,即0-9
    \D 匹配非数字,即不是数字
    \s 匹配空白,即 空格,tab键
    \S 匹配非空白
    \w 匹配单词字符,即a-z、A-Z、0-9、_
    \W 匹配非单词字符
  • 多字符匹配
    字符 功能
    * 匹配前一个字符出现0次或者无限次,即可有可无
    + 匹配前一个字符出现1次或者无限次,即至少有1次
    ? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有
    匹配前一个字符出现m次
    匹配前一个字符出现从m到n次
  • 匹配开头结尾
    字符 功能
    ^ 匹配字符串开头
    $ 匹配字符串结尾
  • 匹配分组
    字符 功能
    | 匹配左右任意一个表达式
    (ab) 将括号中字符作为一个分组
    \num 引用分组num匹配到的字符串
    (?P) 分组起别名
    (?P=name) 引用别名为name分组匹配到的字符串
  • 基本使用
    • 案例1

      import re
      
      ret = re.match("([^-]*)-(\d+)","010-12345678")
      print(ret.group())
      print(ret.group(1))
      print(ret.group(2))
      
      # 运行结果:
      '010-12345678'
      '010'
      '12345678'
      
    • 案例2

      import re
      
      labels = ["<html><h1>www.itcast.cn</h1></html>", "<html><h1>www.itcast.cn</h2></html>"]
      
      for label in labels:
          ret = re.match(r"<(\w*)><(\w*)>.*</\2></\1>", label)
          if ret:
              print("%s 是符合要求的标签" % ret.group())
          else:
              print("%s 不符合要求" % label)
          
      # 运行结果:
      <html><h1>www.itcast.cn</h1></html> 是符合要求的标签
      <html><h1>www.itcast.cn</h2></html> 不符合要求
      
    • 案例3

      import re
      
      ret = re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.itcast.cn</h1></html>")
      print(ret.group())
      
      # 运行结果:
      '<html><h1>www.itcast.cn</h1></html>'
      

re 高级用法

  • 语法规则
    • match(pattern,string): 匹配字符串的开头
      pattern: 正则表达式
      string: 要进行匹配的字符串
      返回一个匹配对象
    • search(pattern, string): 匹配字符串的任意位置
      pattern: 正则表达式
      string: 要进行匹配的字符串
      返回一个匹配对象
    • findall(pattern, string): 匹配所有符合条件的内容
      pattern: 正则表达式
      string:要进行匹配的字符串
      返回所有符合条件的内容的列表
    • sub(pattern, repl, string, count=0): 替换匹配到的内容
      pattern: 正则表达式
      repl:要替换成的内容
      string:要进行替换的字符串
      count:要替换的次数,默认为0,表示无限次
      返回替换后的字符串
    • split(pattern, string[,maxsplit]): 将匹配到的内容作为分割字符进行字符串的分割
      pattern:正则表达式
      string:要进行分割的字符串
      maxsplit:最大分割次数
      返回分割后的字符串列表
  • 案例演示
    • search 使用

      #coding=utf-8
      import re
      
      ret = re.search(r"\d+", "阅读次数为 9999")
      ret.group()
      
      运行结果:
      '9999'
      
    • findall 使用

      #coding=utf-8
      import re
      
      ret = re.findall(r"\d+", "python = 9999, c = 7890, c++ = 12345")
      print(ret)
      
      运行结果:
      ['9999', '7890', '12345']
      
    • sub 使用

      #coding=utf-8
      eg1:
      import re
      
      ret = re.sub(r"\d+", '998', "python = 997")
      print(ret)
      
      运行结果:
      python = 998
      
      
      eg2:
      #coding=utf-8
      import re
      
      def add(temp):
          strNum = temp.group()
          num = int(strNum) + 1
          return str(num)
      
      ret = re.sub(r"\d+", add, "python = 997")
      print(ret)
      
      ret = re.sub(r"\d+", add, "python = 99")
      print(ret)
      
      运行结果:
      python = 998
      python = 100
      
  • 贪婪与非贪婪
    • 基本概念

      Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;
      非贪婪则相反,总是尝试匹配尽可能少的字符。在"*","?","+","{m,n}"后面加上?,使贪婪变成非贪婪。
      
    • 案例演示

      >>> re.match(r"aa(\d+)","aa2343ddd").group(1)
      '2343'
      >>> re.match(r"aa(\d+?)","aa2343ddd").group(1)
      '2'
      >>> re.match(r"aa(\d+)ddd","aa2343ddd").group(1) 
      '2343'
      >>> re.match(r"aa(\d+?)ddd","aa2343ddd").group(1)
      '2343'
      
  • r 的作用
    • 使用演示

      >>> mm = "c:\\a\\b\\c"
      >>> mm
      'c:\\a\\b\\c'
      >>> print(mm)
      c:\a\b\c
      >>> re.match("c:\\\\",mm).group()
      'c:\\'
      >>> ret = re.match("c:\\\\",mm).group()
      >>> print(ret)
      c:\
      >>> ret = re.match("c:\\\\a",mm).group()
      >>> print(ret)
      c:\a
      >>> ret = re.match(r"c:\\a",mm).group()
      >>> print(ret)
      c:\a
      >>> ret = re.match(r"c:\a",mm).group()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: 'NoneType' object has no attribute 'group'
      >>>
      
    • 使用总结

      Python中字符串前面加上 r 表示原生字符串, 与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
      Python里的原生字符串很好地解决了这个问题,有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
      

◀▶ 函数

★ 参数的传入顺序

必备参数 => 默认参数 => 不定长参数 => 关键字参数
  • 案例
    # 函数传入参数顺序    必备 默认 不定长
    def double_sum(x, y=88, *args, **kwargs):
        print(x)
        print(y)
        print(args)
        print(kwargs)
    
    
    double_sum(10, 20, 30, 40, name=10)
    
  • 执行结果
    10
    20
    (30, 40)
    {'name': 10}
    

★ 参数传入的特殊情况

如果很多个值都是不定长参数,那么这种情况下,可以将缺省参数放到 *args的后面, 但如果有**kwargs的话,**kwargs必须是最后的(python3环境下)
  • 案例
    def sum_nums_3(a, *args, b=22, c=33, **kwargs):
        print(a)
        print(b)
        print(c)
        print(args)
        print(kwargs)
    
    sum_nums_3(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900)
    
  • 执行结果
    100
    1
    2
    (200, 300, 400, 500, 600, 700)
    {'mm': 800, 'nn': 900}
    

★ 函数/类方法 形式参数为缺省参数的情况

  • 背景介绍
    函数/类方法 的形式参数已设置缺省参数,且每次调用函数未对形式参数的缺省值进行重新赋值
    形式参数的缺省参数为可变参数;在每次调用该函数时,该形式参数不会被重新赋予函数定义时设置的缺省值,而是延续该函数上次被调用结束后的值,即该形式参数每次被调用后不会释放内存.
    
  • 代码示例
    def sum(a, b, c=[1]):
        print("c的初始值:%s" %c)
        print("c的初始地址:",id(c))
        c += [1]
        print("c加一后的地址:",id(c))
        print("求和值为:%s"% ([a]+[b]+[c]))
        print("求和结束后C的值%s\n"%c)
    
        sum(2,3)
        sum(4,5)
    
  • 执行结果
    c的初始值:[1]
    c的初始地址: 2308199479560
    c加一后的地址: 2308199479560
    求和值为:[2, 3, [1, 1]]
    求和结束后C的值[1, 1]
    
    c的初始值:[1, 1]
    c的初始地址: 2308199479560
    c加一后的地址: 2308199479560
    求和值为:[4, 5, [1, 1, 1]]
    求和结束后C的值[1, 1, 1]
    
  • 总结
    函数的调用可以理解为 将函数对象浅拷贝一份到内存空间,当函数执行完成后cpu将浅拷贝的数据销毁,如果函数体内有可变类型数据变量,直接修改该可变数据变量(即内存级修改,非重新赋值)会导致下次调用该函数时,此变量的值是前一次调用后的值
    

★ 参数描述的语法格式说明

apply_async(func[, args[, kwds]]) 
Process([group [, target [, name [, args [, kwargs]]]]])

"(" : 表示是必传参数,
"[" : 表示可选参数,即已配置缺省参数值
"[, 参数名称" : 表示非第一参数,使用该参数需从第一个依次填写参数,或者使用参数名称赋值(常用)  args=(1,)

◀▶ 面向对象

★ 静态方法、实例方法、类方法

项目 操作对象 调用方式
静态方法 既不操作类也不操作实例对象 类或实例对象
实例方法 操作实例属性 实例对象
类方法 操作类属性 类或实例对象

python私有方法和私有属性理解

  • 规律总结
    1.私有的属性,不能通过对象直接访问,但是可以通过方法访问; 本质是私有属性名被重整(在私有属性名前追加"_类名",而使用方法访问时会自动补全这个前缀)
    2.私有的方法,不能通过对象直接访问(私有方法名重整)
    3.私有的属性、方法,不会被子类继承,也不能被直接访问
    
  • 说明
    1.私有化本质上就是名字重整
    2.一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用; 如果子类需要用到父类的私有属性和方法, 可以在父类中定义公有方法,在子类中间接的访问父类的私有方法、属性
    

★ 类的常见魔法方法

  • __doc__ 类的描述信息
    class Foo:
      """ 描述类信息,这是用于看片的神器 """
    
      def func(self):
        pass
    
    
      print(Foo.__doc__)
    
    """
    <程序执行结果>:
        描述类信息,这是用于看片的神器
    """
    
  • __module__ 当前操作的对象在那个模块
  • __class__ 当前操作的对象在那个模块
    from test import Person
    
    obj = Person()
    print(obj.__module__)  
    print(obj.__class__)
    
    """
    <程序执行结果>:
    test
    <class 'test.Person'>
    """
    
  • __del__ 当对象在内存中被释放时自动触发执行
    class Foo:
    
        def __del__(self):
            print(f"{self} 对象被删除")
    
    
    f1 = Foo()
    del f1
    
    """
    <程序执行结果>:
    <__main__.Foo object at 0x7f96e8125d90> 对象被删除
    """
    
  • __init__ 初始化对象时触发执行
  • __call__ 对象后面加括号时触发执行
    class Foo:
    
        def __init__(self):
            print('__init__ 被调用')
    
        def __call__(self, *args, **kwargs):
            print('__call__ 被调用')
    
    
    f1 = Foo()
    print("--------------")
    f1()
    
    """
    <程序执行结果>:
    __init__ 被调用
    --------------
    __call__ 被调用
    """
    
  • __dict__ 查看对象的属性时触发执行
    class Foo:
    
        COUNT = 0
    
        def __init__(self, name):
            self.name = name
    
        def say_hello(self):
            print(f"hello {self.name} ")
    
    
    f1 = Foo("demo")
    # 获取对象f1的所有属性
    print(f1.__dict__)
    # 获取对象f1类的所有属性
    print(f1.__class__.__dict__)
    """
    <程序执行结果>:
    {'name': 'demo'}
    {'__module__': '__main__', 'COUNT': 0, '__init__': <function Foo.__init__ at 0x7ff3c00e5c20>, 
    'say_hello': <function Foo.say_hello at 0x7ff3e00ca440>, '__dict__': <attribute '__dict__' of 'Foo' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
    """
    
  • __str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
    class Foo1:
        pass
    
    
    class Foo2:
    
        def __str__(self):
            return 'Foo2'
    
    
    f1 = Foo1()
    f2 = Foo2()
    print(f1)
    print(f2)
    
    """
    <程序执行结果>:
    <__main__.Foo1 object at 0x7fe2b000f2d0>
    Foo2
    """
    
  • __xxitem__ 对象索引操作
    class Foo(object):
    
        def __getitem__(self, key):
            print('__getitem__', key)
    
        def __setitem__(self, key, value):
            print('__setitem__', key, value)
    
        def __delitem__(self, key):
            print('__delitem__', key)
    
    
    obj = Foo()
    
    # 对象获取操作,触发 __getitem__
    result = obj['k1']
    # 对象设置操作,触发 __setitem__
    obj['k2'] = 'laowang'
    # 对象删除操作,触发 __delitem__
    del obj['k1']
    
    """
    <程序执行结果>:
    __getitem__ k1
    __setitem__ k2 laowang
    __delitem__ k1
    """
    
  • slice 对象切片操作
    """
    Python3 的官方文档可知,Python3中的切片的魔术方法不再是Python2中的getslice(), setslice()和delslice(),而是借助 slice 类整合到了getitem(),setitem()和 delitem()中。
    """
    
    class Foo(object):
        def __getitem__(self, index):
            if isinstance(index, slice):
                print(f"【slice-get】>> start: {index.start}, stop: {index.stop}, step: {index.step}")
    
        def __setitem__(self, index, value):
            if isinstance(index, slice):
                print(f"【slice-set】>> start: {index.start}, stop: {index.stop}, step: {index.step}")
                print(f"【slice-set】>> The value is:", value)
    
        def __delitem__(self, index):
            if isinstance(index, slice):
                print(f"【slice-del】>> start: {index.start}, stop: {index.stop}, step: {index.step}")
    
    
    obj = Foo()
    
    # 对象切片获取操作
    obj[-1:1]
    # 对象切片修改操作
    obj[-1:10:1] = [11, 22, 33, 44]
    # 对象切片删除操作
    del obj[-1:10:2]
    
    """
    <程序执行结果>:
    【slice-get】>> start: -1, stop: 1, step: None
    【slice-set】>> start: -1, stop: 10, step: 1
    【slice-set】>> The value is: [11, 22, 33, 44]
    【slice-del】>> start: -1, stop: 10, step: 2
    """
    

property 属性

  • 基本定义
    一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法
    
  • property 属性的两种方式
    • 装饰器 => 在方法上应用装饰器
    • 类属性 => 在类中定义值为property对象的类属性
  • 装饰器方式
    • 代码示例

      class Goods(object):
      
          def __init__(self):
              # 原价
              self.original_price = 100
              # 折扣
              self.discount = 0.8
      
          @property
          def price(self):
              # 实际价格 = 原价 * 折扣
              new_price = self.original_price * self.discount
              return new_price
      
          # 新式类独有
          @price.setter
          def price(self, value):
              self.original_price = value
      
          # 新式类独有
          @price.deleter
          def price(self):
              del self.original_price
      
      obj = Goods()
      obj.price         # 获取商品价格
      obj.price = 200   # 修改商品原价
      del obj.price     # 删除商品原价
      
    • 示例说明

      1.定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
      2.经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
      3.新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法;我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
      
    • 实例应用

      class Pager:
          def __init__(self, current_page):
              # 用户当前请求的页码(第一页、第二页...)
              self.current_page = current_page
              # 每页默认显示10条数据
              self.per_items = 10 
      
          @property
          def start(self):
              val = (self.current_page - 1) * self.per_items
              return val
      
          @property
          def end(self):
              val = self.current_page * self.per_items
              return val
      
      p = Pager(1)
      p.start  # 就是起始值,即:m
      p.end  # 就是结束值,即:n
      
      # Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回。
      
  • 类属性方式
    • 代码示例

      class Goods(object):
      
          def __init__(self):
              # 原价
              self.original_price = 100
              # 折扣
              self.discount = 0.8
      
          def get_price(self):
              # 实际价格 = 原价 * 折扣
              new_price = self.original_price * self.discount
              return new_price
      
          def set_price(self, value):
              self.original_price = value
      
          def del_price(self):
              del self.original_price
      
          PRICE = property(get_price, set_price, del_price, '价格属性描述...')
      
      obj = Goods()
      obj.PRICE         # 获取商品价格
      obj.PRICE = 200   # 修改商品原价
      del obj.PRICE     # 删除商品原价
      
    • 示例说明

      1.当使用类属性的方式创建property属性时,经典类 和 新式类 无区别
      2.property方法中有个四个参数
      	第一个参数是方法名,调用 对象.属性 时自动触发执行方法
      	第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
      	第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
      	第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
      3.由于 类属性方式 创建property属性具有3种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
      

__dict__dir()

Python下万物皆对象,每个对象都有多个属性(attribute),Python对属性有一套统一的管理方案。
1. dir(对象名)是一个函数,返回的是list;
2. __dict__是一个属性,返回的是dictionary,键为属性名(这里的属性包含方法),值为属性值;
3. dir()用来寻找一个对象的所有属性,包括__dict__中的属性,__dict__是dir()的子集;
4. __dir__是魔法方法, 实例.__dir__() 与 dir(实例)  等效

* 并不是所有对象都拥有__dict__属性。许多内建类型就没有__dict__属性,如list,此时就需要用dir()来列出对象的所有属性。
* 实例的__dict__仅存储与该实例相关的实例属性(不含实例方法,因为实例方法是保存在类里面的),正是因为实例的__dict__属性,每个实例的实例属性才会互不影响。	
*类的__dict__存储所有实例共享的变量和函数(类属性,方法等),类的__dict__并不包含其父类的属性。

*dir(对象名)函数会自动寻找一个对象的所有属性和方法(包含私有方法),包括__dict__中的属性。
*__dict__是dir()的子集,dir()包含__dict__中的属性。

◀▶ 模块导入

★ 方式一:from xx import yy

  • 导入方式
    from info import redis_store
    
  • 使用说明
    【理解】: 相当于在本地定义一个变量redis_store 指向info模块中redis_store的值,对本地的redis_stone直接进行赋值,只会修改本地变量redis_store的指向,并不会修改info模块中redis_store的值;这里如果info模块中redis_store是可变类型数据(如list()),在本地中通过from info import redis_store导入后直接对其指向的值进行修改(如redis_store.append()),才会修改info模块中redis_store的值
    

★ 方式二:import xx

  • 导入方式
    import info
    
    info.redis_store
    
  • 使用说明
    【理解】: 使用 import info,相当于在本地定义一个变量info 指向模块info,但是它保持着自已的名字空间,这就是为什么你需要使用模块名来访问它的函数或属性: module.function 的原因
    说明 导包或者导入包内变量,python解释器会将整个文件运行一次
    

★ 方式三:__import__("xx")

  • 导入方式
    __import__("xx")
    
  • 使用说明
    【理解】:返回模块名叫info的模块(模块也是对象)
    

★ 方式四:import_module(module_path)

  • 文件结构
    imp
    │───demo.py
    │
    └───apps
        └───api
            └───urls.py
    
  • 代码实例
    • urls.py

      urlpatterns = []
      app_name = "api"
      
    • demo.py

      # 1. 基础导入模式
      from apps.api import urls
      
      print(urls.urlpatterns, urls.app_name)
      
      # 2. 字符串导入模式
      import importlib
      
      module_path = 'apps.api.urls'
      module = importlib.import_module(module_path)
      print(module.urlpatterns, module.app_name)
      
      """
      执行结果:
      [] api
      [] api
      """
      

reload 重复导入

  • 背景说明
    模块被导入后,"import module/from xx import yy" 不能重新导入模块,重新导入需要使用"reload", 也就是说如果对被导入的包中数据进行修改,即便使用import重新导入,正在运行的代码使用的仍然是原来包中的数据。(导包实际上是将硬盘中的数据加载到内存,reload 可以将修改的包数据重新加载到内存中)
    
  • 代码实例
    • test.py

      a = 10
      
    • main1.py

      import test
      import time
      
      while True:
          time.sleep(1)
          import test
      
          print(test.a)
      
      """
      修改test.py 文件中变量a 的值并保存
      <程序执行结果>:
      10
      10
      10
      10
      """
      
    • main2.py

      import importlib
      import test
      import time
      
      while True:
          time.sleep(1)
          # 重新导入test
          importlib.reload(test)
          print(test.a)
      
      """
      修改test.py 文件中变量a 的值并保存
      <程序执行结果>:
      10
      10
      10
      10
      100
      100
      2000
      """
      

pyc文件作用说明

在这个过程中这些字节码都是在内存中的,众所周知Python的运行性能不如编译性语言(比如C语言,JAVA …),所以Python在程序执行结束后会把字节码写入到硬盘中,保存为.pyc文件,目的是下一次再执行 `python xxx.py`程序时,Python会先在目录下找`xxx.pyc`文件来执行,因为.pyc文件里保存的是字节码,所以就节省了Python解析器把`xxx.py`翻译成字节码的时间,所以就提高了性能。

◀▶ 编码

unicode 编码

  • 示例
    # 将字符串 "你" 进行 unicode 编码
    print("你".encode("unicode_escape"))
    
  • 执行结果
    b'\\u4f60'
    

★ 字符与 unicode 编码互转

  • 示例
    # ord: 它以一个字符(长度为1的字符串)作为参数,返回对应的 Unicode 数值
    # chr: 它接受一个整数参数,并返回一个对应的字符。它的作用是将整数转换为相应的Unicode字符
    print(ord("你"))
    print(chr(20320))
    
  • 执行结果
    20320
    你
    

◀▶ 进制转换

★ 基本进制转换

  • 转换语法
    bin(): 将整数转换为二进制字符串。
    oct(): 将整数转换为八进制字符串。
    hex(): 将整数转换为十六进制字符串。
    int(): 将字符串表示的其他进制数转换为十进制整数。
    
  • 示例
    # 将一个数字从十进制转换为八进制
    print(oct(42))
    # 将一个数字从十进制转换为十六进制:
    print(hex(42))
    
    # 将十六进制的 "4f60" 转换为 十进制
    print(int("4f60", 16))
    # 将八进制的 "11" 转换为 十进制
    print(int("11", 8))
    
  • 执行结果
    0o52
    0x2a
    20320
    9
    

◀▶ 单例模式

★ 单例模式基本介绍

单例模式是一种设计模式,用于确保一个类只有一个实例,并提供全局访问点以获取该实例。它是一种创建型模式,通常用于需要严格控制某个类的实例数量的情况。单例模式确保一个类在整个应用程序生命周期中只有一个实例,因此可以节省系统资源,同时提供了一个集中的访问点,以便在需要时获取该实例。

★ 实现单例的几种方式:

  • 模块导入
  • 添加装饰器(类装饰器和函数装饰器)
  • 重写new方法
  • 元类继承

★ 方式一: 模块导入

  • 文件结构
    单例
    ├───file1.py
    ├───file2.py
    ├───readme
    ├───单例实现1_模块导入.py
    └───模块导入实现单例测试.py
    
  • 单例实现1_模块导入.py
    """
    模块导入实现单例模式步骤:
    1. 在模块中定义类
    2. 实例化类并返回
    3. 在其他文件中导入实例对象使用, 每个文件导入的对象实际是同一个
    """
    
    
    class Singleton:
        def __init__(self, name):
            self.name = name
    
        def do_something(self):
            pass
    
    
    singleton = Singleton('模块单例')
    
    # 在其他py文件中
    # from my_singleton import singleton
    
  • file1.py
    from 单例实现1_模块导入 import singleton
    
    print(singleton)
    
  • file2.py
    from 单例实现1_模块导入 import singleton
    
    print(singleton)
    
  • 模块导入实现单例测试.py
    import file1
    import file2
    
    print(file1.singleton is file2.singleton)
    
  • 执行结果
    <单例实现1_模块导入.Singleton object at 0x0000021B2B81F400>
    <单例实现1_模块导入.Singleton object at 0x0000021B2B81F400>
    True
    

★ 方式二: 装饰器

  • 单例实现2_装饰器.py
    # -------------------函数装饰器---------------------------
    def Singleton1(cls):
        instance = {}
    
        def _singleton_wrapper(*args, **kargs):
            if cls not in instance:
                instance[cls] = cls(*args, **kargs)
            return instance[cls]
    
        return _singleton_wrapper
    
    
    # -------------------类装饰器---------------------------
    class Singleton2:
    
        def __init__(self, cls):
            self.cls = cls
            self._instance = None
    
        def __call__(self, *args, **kwargs):
            if not self._instance:
                self._instance = self.cls(*args, **kwargs)
            return self._instance
    
    
    # SingletonTest = Singleton1(SingletonTest) =_singleton_wrapper
    # SingletonTest = Singleton2(SingletonTest) = Singleton2实例对象
    @Singleton1
    class SingletonTest(object):
        def __init__(self, name):
            print(">>> 初始化 <<<")
            self.name = name
    
    
    s1 = SingletonTest('s1')
    s2 = SingletonTest('s2')
    print(s1, s2)
    print(s1 is s2)
    
    
    
  • 执行结果
    >>> 初始化 <<<
    <__main__.SingletonTest object at 0x000001E6A2FF73D0> <__main__.SingletonTest object at 0x000001E6A2FF73D0>
    True
    

★ 方式三: 重写new方法

  • 单例实现3_重写new方法.py
    class Singleton(object):
    
        def __new__(cls, *args, **kwargs):
            if not hasattr(Singleton, "_instance"):
                Singleton._init_flag = True
                Singleton._instance = super().__new__(cls)
            return Singleton._instance
    
        def __init__(self, name):
            if not hasattr(Singleton, "_init"):
                Singleton._init = True
                print(">>> 初始化 <<<")
                self.name = name
    
    
    s1 = Singleton('s1')
    s2 = Singleton('s2')
    print(s1, s2)
    print(s1 is s2)
    
  • 执行结果
    >>> 初始化 <<<
    <__main__.Singleton object at 0x0000016663140760> <__main__.Singleton object at 0x0000016663140760>
    True
    

★ 方式四: 元类继承

  • 单例实现4_元类继承.py
    class Singleton(type):
    
        def __call__(cls, *args, **kwargs):
            if not hasattr(Singleton, "_instance"):
                # cls 是 Singleton 创建的类
                Singleton._instance = cls.__new__(cls, *args, **kwargs)
                cls.__init__(Singleton._instance, *args, **kwargs)
            return Singleton._instance
    
    
    class SingletonTest(metaclass=Singleton):
        pass
    
    
    class A(SingletonTest):
        def __init__(self, name):
            print(">>> 初始化 <<<")
            self.name = name
    
    
    s1 = A('s1')
    s2 = A('s2')
    print(s1, s2)
    print(s1 is s2)
    
  • 执行结果
    >>> 初始化 <<<
    <__main__.A object at 0x000001687C79D5E0> <__main__.A object at 0x000001687C79D5E0>
    True
    

◀▶ 深浅拷贝

★ 浅拷贝

  • 使用方式
    import copy
    
    # 浅拷贝
    copy.copy()
    
  • 拷贝原则
    • 对可变类型对象进行浅拷贝, 只做顶层拷贝
    • 对不可变类型对象进行浅拷贝, 那么不拷贝

★ 深拷贝

  • 使用方式
    import copy
    
    # 深拷贝
    copy.deepcopy()
    
  • 拷贝原则
    • 对可变类型对象进行深拷贝, 除了顶层拷贝,还会对该对象的子元素进行深拷贝
    • 对不可变类型对象进行深拷贝(递归思维,以递归出口为最终 是否执行拷贝操作的依据)
      • 如果该对象存在可变类型数据的子元素, 那么会顶层拷贝, 并且对其子元素进行深拷贝
      • 如果该对象不存在可变数据对象, 那么不拷贝

★ 深浅拷贝总结

浅拷贝是顶层拷贝,深拷贝是递归拷贝; 对于不可变数据,如果该对象不存在可变数据对象, 那么深浅拷均不拷贝
posted @ 2024-04-02 22:43  CSMrDong  阅读(118)  评论(0编辑  收藏  举报