15.python知识总结(更新中)

目录

目录

一:docString

二:python脚本接受控制台参数

三:global 修饰符

四:模块的概念

五:   .pyc文件

 六:__name__

6.1直接运行temp2.py 时:__name__="__main__"​编辑

6.2:实际项目的应用

6.3:出现问题

6.4 大体总结、这时只要在测试内容前面加上:if __name__ == '__main__':

七:dir() 函数和__dict__ 属性的作用

八:复杂数据类型

    8.1 元祖

   8.2 list列表

8.4字典

九:元祖或列表做函数参数

 十一:关于python作用域

十一:String

十二:locals()   globals()

十八:魔法函数

A.  __new__

十九 cls()函数




一:docString

DocStrings 文档字符串是一个重要工具,用于解释文档程序,其实就是对程序方法的一些说明信息。我们可以在函数体的第一行使用一对三个单引号 ''' 或者一对三个双引号 """ 来定义文档字符串。
你可以使用  " 函数名.__doc__  "(注意双下划线)调用函数中的文档字符串属性。   DocStrings 文档字符串使用惯例:它的首行简述函数功能第二行空行,第三行为函数的具体描述。

#!/usr/bin/python
# Filename: func_doc.py
def printMax(x, y):
 '''Prints the maximum of two numbers.
 The two values must be integers.'''
 x = int(x) # convert to integers, if possible
 y = int(y)
 if x > y:
 print x, 'is maximum'
 else:
 print y, 'is maximum'


printMax(3, 5)
print printMax.__doc__ 



#下面执行脚本  func_doc.py  中调用了printMax(3, 5) 和 print printMax.__doc__ 
#print printMax.__doc__   会把我们的注释打印出来 这就是docString
$ python func_doc.py
5 is maximum
Prints the maximum of two numbers.
 The two values must be integers.

二:python脚本接受控制台参数

记住,脚本的名称(文件名称)总是sys.argv列表的第一个参数。所以,在下面,'using_sys.py'是sys.argv
[0]、'we'是sys.argv[1]、'are'是sys.argv[2]以及'arguments'是sys.argv[3]。注意,Python从0开始计
数,而非从1开始。
#!/usr/bin/python
# Filename: using_sys.py
import sys
print 'The command line arguments are:'
for i in sys.argv:
 print i

#执行程序
$ python using_sys.py  we are arguments

#输出结果
The command line arguments are:

using_sys.py
we
are
arguments

三:global 修饰符

没有global语句,是不可能在函数内部修改定义在函数外  的变量。global语句被用来声明x是全局的。  类似于值传递  引用传递的概念
 

#!/usr/bin/python
# Filename: func_global.py
def func():
 global x
 print 'x is', x
 x = 2



x = 50
func()
print 'x is', x 

#执行脚本
$ python func_global.py
#输出结果
x is 50
x is 2

四:模块的概念

模块基本上就是一个 包含了所有你定义的函数和变量的文件。为了在其他程序中重用模块,模块的文件名必须以.
py为扩展名。  模块可以从其他程序 输入 以便利用它的功能。这也是我们使用Python标准库的方法。首先,
我们将学习如何使用标准库模块。就是用import来引入模块,可以导入整个模块,也可以导入模块文件中的某些方法。
  1. from 模块名 import  方法名    :这种导入方式是导入的某个方法。 调用方法是  " 方法名 "
  2. import 模块名   :这种导入方式  将整个模块导入当前工作空间。调用方法是  " 模块名.方法名 "

   推荐第二种,因为不同模块有可能具有同名的方法, 为了更加细节的区分  最好曹勇模块名.方法的调用方式,避免产生混淆现象。

五:   .pyc文件

输入一个模块相对来说是一个比较费时的事情,所以Python做了一些技巧,以便使输入模块更 加快一些。一种方法是创建 字节编译的文件 ,这些文件以.pyc作为扩展名。字节编译的文件与 Python变换程序的中间状态有关。当你在下次从别的 程序输入这个模块的时候,.pyc文件是十分有用的——它会快得多,因为一部分输入模块所需 的处理已经完成了。另外,这些字节编译的文件也是与平台无关的。所以,现在你知道了那 些.pyc文件事实上是什么了。

 六:__name__

Python源码中,通常会有一句if __name__ == '__main__':, 本文将介绍其作用,以及实际项目开发中的应用。

__name__是python的一个内置类属性,它被内嵌每一个model模块中,代表对应程序名称。

例如下图源码temp2.py:

6.1直接运行temp2.py 时:__name__="__main__"
在这里插入图片描述

B、二在其它程序中导入temp2.py,则:__name__="temp2.py"
在这里插入图片描述


从例子中的输出内容可以看出,直接运行py文件和导入py文件输出的__name__值不同,因此通过判断__name__的值,就可以区分py文件是直接被运行,还是被引入到其他程序中。


6.2:实际项目的应用

具体情境:

1、项目领导给你安排一个任务,写一个生成组合列表的模块。

2、通常编写完成程序代码,会在结尾写上一些测试信息,来验证程序是否正确。如图:

# BuildComb.py
from itertools import combinations
def build_combination(n,k):
    result = combinations(range(n),k)
    return result
    
#-----------------------------------------------------------------------
#以下为针对模块的测试信息
a = build_combination(4, 3)
for i in a:
    print(i)

6.3:出现问题

调用该模块的效果


**当然,也可以在交付领导之前删除测试相关内容。只是通常领导可能会发现模块的问题,或者是业务需求本身有调整。那么再次修改模块,就需要再次添加这些测试信息,交付的时候再次删除它。如果是复杂一些的模块,那就得反反复复的"再再再",很明显不是一个完美的解决方法。

6.4 大体总结、这时只要在测试内容前面加上:if __name__ == '__main__':

# BuildComb.py
from itertools import combinations
def build_combination(n,k):
    result = combinations(range(n),k)
    return result
    
if __name__ == '__main__': #程序被导入,以下代码不被执行
	a = build_combination(4, 3)
	for i in a:
	    print(i)

那么,编写调试过程直接运行该模块时__name__ = "__main__",测试内容被执行。

而领导调用它,__name__ = "BuildComb",测试内容则不会被执行,完美的解决了这个问题。

七:dir() 函数和__dict__ 属性的作用

1: python 内置方法有很多,无论是初学者还是精通python 的程序员都不能全部即住所有的方法,这时候 dir() 方法就非常有用了,使用 dir()函数可以查看对象内的所有的属性和方法,在 python 中任何东西都是对象,一种数据类型,一个模块等,都有子集的属性和方法,除了常用的方法外,其他的你不需要全部记住它,交给 dir() 函数就好了。

2: 在 Python 类的内部,无论是类属性还是实例属性,都是以字典的形式进行存储的,其中属性名作为键,而值作为该键对应的值。

为了方便用户查看类中包含哪些属性,Python 类提供了 __dict__ 属性。需要注意的一点是,该属性可以用类名或者类的实例对象来调用,用类名直接调用 __dict__,会输出该由类中所有类属性组成的字典;而使用类的实例对象调用 __dict__,会输出由类中所有实例属性组成的字典。
 

dir("")  
dir("sys")
dir([])
dir({})

八:复杂数据类型

元组和列表十分类似,只不过元组和字符串一样是不可变的即你不能修改元组。元组通过圆
括号中用逗号分割的项目定义。元组通常用在使语句或用户定义的函数能够安全地采用一组值
的时候,即被使用的元组的值不会改变。  对元祖和字符串的操作  返回的都是内部新建的元祖和字符串,这就是不可变性。

    8.1 元祖

元祖和list不同的是,元祖是不可更改的,你可以理解为一旦元祖被创建,
他的内存空间就被锁定了,不能往里面添加新的元素,当然也取不出来。  另外元组中每个元素必须用逗号分隔,即便该元组只有一个数据,也得在后面跟着一个逗号,比如 name=("天线宝宝",)
判断元组中是否包含某个元素:in 
判断某个元素是否不在元组中:not in

元组存在的意义: 元祖和list都是存放数据的,不过如果在写程序的时候你需要一种不能被更改的数组来存数据,那就首选元祖,这样做能让你的程序一目了然。
别人一看就知道你这个数据是固定的。  这就很有意义了,比如 经纬度数据,全国省份的名字。。等类似的不可更改的数据采用元祖存储,要比用list存储更加清晰

   8.2 list列表

初始化空list 有两种方式:
    1:listOne=list()
    2: listOne =[]


list=['apple','mango','carrot','banana']
添加: list.append("girl")
排序: shoplist.sort()
删除: del shoplist[3]
拼接合并:可以用"+"  符号拼接两个列表 返回一个新的列表: listThree=listOne+listTwo
判断某个元素是否在list中: 
         listOne=[1,2,3]
         print (3  in listOne)
         if 3 in listOne:
             print "存在3"
      结果:True
            存在
判断某个元素是否不在list中:if 3 not in listOne:
     

list是可变的所有的操作都是基于其自身的修改,这一点和string  元组都不同(可以理解为指针操作,熟悉java  c++的应该明白什么意思,不懂的话建议查看一下值传递  引用传递)

8.4字典

首先字典是可变的,类似于java里面的map集合。其实这两个本质上是一个概念,不过python就不称作map而是用了 "字典" 的名字

#!/usr/bin/python
 
dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
 
dict['Age'] = 8 # 更新
dict['School'] = "RUNOOB" # 添加

 
del dict['Name']  # 删除键是'Name'的条目
dict.clear()      # 清空字典所有条目
del dict          # 删除字典

九:元祖或列表做函数参数

在函数中接收元组和列表
当要使函数接收元组或字典形式的参数的时候,有一种特殊的方法,它分别使用*和**前缀。
这种方法在函数需要获取可变数量的参数的时候特别有用。
由于在args变量前有*前缀,所有多余的函数参数都会作为一个元组存储在args中。如果使用的
是**前缀,多余的参数则会被认为是一个字典的键/值对。

 十一:关于python作用域

  1. if/elif/else、try/except/finally、 for/while 并不能涉及变量作用域的更改,也就是说他们的代码块中的变量,在外部也是可以访问的。这一点和java  c++尤其不一样,格外注意。
  2. def  class  lambda  是具有绝对作用域的

这里我就不多写测试例子,只写一个暴力的,比如在外层try  /except  访问for循环内部变量,  这种对java来说不可能的在python很简单,看下面。

diccc={"张三":"男","李四":"男","魏公共":"位置","李司棋":"女",None:"你猜","姚明":"男"}
try:
    for i in diccc.keys():
        print "%s 成功 value:%s"%(i,diccc[i])
        if i is None:
            raise Exception("存在不可理的值")
except Exception as e:
    print "%s 出现错误 %s"%(diccc[i],e.message)

十一:String

python2  与python3 关于String是有区别的:

python2中字符串有两种类型: unicode(显示格式)、str(存储格式):
在Python2中,字符串无法完全地支持国际字符集和Unicode编码。为了解决这种限制,Python2对Unicode数据使用了单独的字符串类型。
要输入Unicode字符串字面量。要在第一个引号前加上’u’。Python2中普通字符串实际上就是已经编码(非Unicode)的字节字符串。

python3中两种字符串类型:str : unicode的呈现形式  bytes :字节类型,互联网上数据的都是以二进制的方式(字节类型)传输的。
str 使用encode方法转化为 bytes  bytes 通过decode转化为 str 编码方式解码方式必须一样,否则就会出现乱码
总结:python 2中的字符串实际上是编码后的字节串b_str    python3为了统一,字符串是unicode字符串

python 对字符串支持索引操作,支持迭代操作:

 
   s="name"       
   s[0] = n    s[-1]= e  
   s[1] = a    s[-2]= m      
   s[2] = m    s[-3]= a
   s[3] = e    s[-4]= n


字符串和元祖一样都是不可修改的,如果要进行修改那基本上返回的都是一个新的字符。
支持迭代操作:
            s="abcdefg"
            for i in s:
                print i

            输出: 
                 a
                 b
                 c
                 d
                 e
                 f
                 g            

拼接:用 "+"即可
小功能: a *3=aaa 
大小写转换: AAA.lower=aaa   aaa.upper()=AAA
首字母改成大写:aaa.capitalize()=Aaa
format:  "{} world is {}".format(the,pretty)  ="the world is pretty"  {}相当于占位符
split: 指定用符号切分str  返回数组,这个方法和很多语言的功能一样:
       "a,b,c,d".split(",")=[a,b,c]
  
 

 

十二:locals()   globals()

  1. 这两个函数主要提供,基于字典的方式访问  局部变量和全局变量的方式

python 使用叫做名字空间的东西来记录变量的轨迹。名字空间是一个字典 ,它的键就是字符串形式的变量名字,它的值就是变量的实际值名字空间可以像 Python 的 dictionary 一样进行访问。

在一个 Python 程序中的任何一个地方,都存在几个可用的名字空间。

每个函数都有着自已的名字空间,叫做局部名字空间,它记录了函数的变量,包括函数的参数和局部定义的变量。

每个模块拥有它自已的名字空间,叫做全局名字空

def foo(arg, a):
    x = 100
    y = 'hello python!'
    for i in range(10):
        j = 1
        k = i
        print locals()
foo(1,2)

输出结果如下:可以看到所有的局部变量都在字典中被保存,local只是复制了一份供你查看,你是不能借助locals修改局部变量的,也不允许你去修改局部变量。
{'a': 2, 'i': 0, 'k': 0, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 1, 'k': 1, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 2, 'k': 2, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 3, 'k': 3, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 4, 'k': 4, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 5, 'k': 5, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 6, 'k': 6, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 7, 'k': 7, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 8, 'k': 8, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}
{'a': 2, 'i': 9, 'k': 9, 'j': 1, 'arg': 1, 'y': 'hello python!', 'x': 100}

locals 是只读的,不可修改, 而globals可以修改,原因是:
  locals()实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行修改,修改的是拷贝,而对实际的局部名字空间中的变量值并无影响。globals()返回的是实际的全局名字空间,而不是一个拷贝: 与 locals 的行为完全相反。
所以对 globals 所返回的 dictionary 的任何的改动都会直接影响到全局变量的取值。

下面看例子:
'''This is my first python program!'''
z = 7 #定义全局变量
def foo(arg):
    x = 1
    print locals()
    print 'x=',x
    locals()['x'] = 2 #修改的是局部名字空间的拷贝,而实际的局部名字空间中的变量值并无影响。
    print locals()
    print "x=",x
# foo(3)
print globals()
print 'z=',z
globals()["z"] = 8 #globals()返回的是实际的全局名字空间,修改变量z的值
print globals()
print "z=",z
{'foo': <function foo at 0x000000000317CAC8>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'C:/Users/Administrator/IdeaProjects/Pydemo/pytest/test2.py'
, '__package__': None, '__name__': '__main__', 'z': 7, '__doc__': "''This is my first python program!"}
z= 7
{'foo': <function foo at 0x000000000317CAC8>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'C:/Users/Administrator/IdeaProjects/Pydemo/pytest/test2.py'
, '__package__': None, '__name__': '__main__', 'z': 8, '__doc__': "''This is my first python program!"}
z= 8

,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。还有就是内置名字空间, 任何模块均可访问它,它存放着内置的函数和异常。

  1. 当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:

局部名字空间 - 特指当前函数或类的方法。如果函数定义了一个局部变量 x, 或一个参数 x,Python 将使用它,然后停止搜索。全局名字空间 - 特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。

内置名字空间 - 对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。

如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,同时传递 There is no variable named 'x' 这样一条信息.

     2.名字空间在运行时直接可以访问。局部名字空间可以通过内置的 locals 函数来访问。全局 (模块级别) 名字空间可以通过内置的 globals 函数来访问。

locals 对局部 (函数) 名字空间做了些什么,globals 就对全局 (模块) 名字空间做了什么。

然而 globals 更令人兴奋,因为一个模块的名字空间包含了模块级的变量和常量,它还包括了所有在模块中定义的函数和类,以及任何被导入到模块中的东西。

3.回想一下 from module import 和 import module 之间的不同?

使用 import module,模块自身被导入,但是它保持着自已的名字空间,这就是为什么您需要使用模块名来访问它的函数或属性: module.function 的原因。

但是使用 from module import,实际上是从另一个模块中将指定的函数和属性导入到您自己的名字空间,这就是为什么您可以直接访问它们却不需要引用它们所来源的模块的原因。使用 globals 函数,您会真切地看到这一切的发生。

十八:魔法函数

所谓的魔法函数是网络用语,本质上其实就是这些函数是内置的,且其调用是隐士调用的,调用的时机都是不同的。

A.  __new__

大家应该对__init__()方法都很熟悉,它的第一个参数一定是self,__init__()方法负责对象的初始化,系统执行该方法前,其实该实例对象已经存在,要不然初始化什么呢.通常来说,类开始实例化时,__new__()方法会返回cls(cls指代当前类)的实例然后该类的__init__()方法会接收这个示例(即self)作为自己的第一个参数,然后依次转入__new__()方法中接收的位置参数和命名参数。

class Dog():
    def __new__(cls, *args, **kwargs):
        print("run the new of dog")
        #return super(Dog,cls).__new__(cls)
        return object.__new__(cls)  #两条return语句作用相同

    def __init__(self):
        print("run the init of dog")
        print(self)
        print(self.__class__)

a = Dog()

# run the new of dog
# run the init of dog
# <__main__.Dog object at 0x00000197AAA3A8D0>
# <class '__main__.Dog'>

可以看出,

1.当我实例化Dog类对象时,
    python中首先调用的是类对象的__new__()方法,
    如果该对象没有定义__new__()方法,
    则去父类中依次查找,直到object类(object类是所有类的基类哦)。
2. __new__()的返回语句中,
    object.__new__(cls)意思是调用父类(object)的__new__(),
    super()是一个特殊函数
    ,帮助python将父类和子类关联起来,
    父类也成超类,名称super因此得名。

3. __new__()需要传递一个参数cls,
    __init__()需要传递一个参数self,
    self代表的就是实例对象本身,
    cls代表的是类对象本身。python中的self相当于C++的this指针。
    __new__()必须要有返回值,返回实例化出来的实例对象。
4.一般我们不会去重写__new__()方法,除非你确切知道怎么做,
什么时候你会去关心它呢,
它作为构造函数用于创建对象,是一个工厂函数,
专用于生产实例对象。著名的设计模式之一,
单例模式,就可以通过此方法来实现。

如果__new__()没有返回cls(即当前类的实例),那么当前类的__init__()方法是不会被调用的,如果__new__()返回了其他类的实例,那么只会调用被返回的那个类的构造方法。

class A(object):
    def __init__(self, *args, **kwargs):
        print("run the init of A")
    def __new__(cls, *args, **kwargs):
        print("run thr new of A")
        return object.__new__(B, *args, **kwargs)

class B(object):
    def __init__(self):
        print("run the init of B")
    def __new__(cls, *args, **kwargs):
        print("run the new of B")
        return object.__new__(cls)

a = A()
print(type(a))
# run thr new of A
# <class '__main__.B'>
b = B()
print(type(b))
# run the new of B
# run the init of B
# <class '__main__.B'>

其实__new__ 中的cls就是类本身,不是实例哦是类本身,下面看一个单例的实现:

# 实例化一个单例
class Singleton(object):
    __instance = None

    def __new__(cls, age, name):
        #如果类数字__instance没有或者没有赋值
        #那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时
        #能够知道之前已经创建过对象了,这样就保证了只有1个对象
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance
    
    def __init__(self,age,name):
        self.age = age
        self.name = name
a = Singleton(18, "wk")
b = Singleton(8, "mm")

print(id(a)==id(b))
print(a.age,a.name)
print(b.age,b.name)
a.size = 19 #给a指向的对象添加一个属性
print(b.size)#获取b指向的对象的age属性

# True
# 8 mm
# 8 mm
# 19


对上面的代码优化下,只执行一次__init__:
# 实例化一个单例
class Singleton(object):
    __instance = None
    __first_init = False

    def __new__(cls, age, name):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def __init__(self,age,name):
        if not self.__first_init:
            self.age = age
            self.name = name
            Singleton.__first_init = True

a = Singleton(18, "wk")
b = Singleton(8, "mm")

print(id(a)==id(b))
print(a.age,a.name)
print(b.age,b.name)
a.size = 19 #给a指向的对象添加一个属性
print(b.size)#获取b指向的对象的age属性

# True
# 18 wk
# 18 wk
# 19

总结:

  • __init__()通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性,做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
  • __new__()通常用于控制生成一个新实例的过程。它是类级别的方法。
  • __new__()至少有一个参数cls,代表要实例化的类,此参数在实例化时会有python编辑器自动提供。
  • __new__()必须有返回值,返回实例化出来的实例。
  • 如果将类比作制造商,__new__()方法发就是前期的原材料环节,__init__()方法就是在有了原材料的基础上,加工,初始化商品的环节。

十九 cls()函数

在看本小节之前请确保先看上一章内容

cls()函数可用来直接创建对象,底层其实就是调用__new__ 

class Demo(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
        print('__init__() called...')

    def __new__(cls, name, age):
        """

        :type name: str
        """
        print('__new__() - {cls}'.format(cls=cls))
        return object.__new__(cls)
    @classmethod
    def bu(cls,name,age):
        return cls(name,age)

if __name__ == '__main__':
    # de = Demo(age=0,name="999")
    # print(de.name)
    # print(de.age)
   s =  Demo.bu(name="张三", age=180)
   print(s.name)

输出结果为:

__new__() - <class '__main__.Demo'>
__init__() called...
张三

二十. 多继承中的__new__ __init__

1. __init__的调用链和__new__的调用链相反,__new__从子类开始,__init__从父类开始

因此最终属性的赋值由子类的__init__生效,这其实就是子类覆盖了父类的属性的概念

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我先森

鼓励一个吧,哈哈

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值