Python3基础之模块、面向对象等知识点搜集(二) - Go语言中文社区

Python3基础之模块、面向对象等知识点搜集(二)


目录

补充:__import__ 和 __init__的理解 如何import非第三方库之外的py文件

定义类(init只负责初始化)

保护对象的属性(getter setter)

__call__()方法

__del__() 方法

单继承

多继承

类属性和实例属性

动态添加和__slots__

类方法

静态方法

工厂模式

__new__方法(只负责创建)

单例模式和只初始化一次

系统第三方模块

自建模块

列表生成式

深Copy和浅Copy

property

迭代器

闭包

装饰器

作用域

生成器

元类

内建属性

__getattribute__的坑

内建函数

functools

常用系统模块

hashlib

正则表达式


 

补充:__import__ 和 __init__的理解 如何import非第三方库之外的py文件

第一种:__import__

首先:两个py文件都在同一目录下,我们可以直接import来用,但是在不同文件目录下为什么不能直接import?


In [1]: import sys

In [2]: sys.path
Out[2]: 
['',
 '/Library/Frameworks/Python.framework/Versions/3.6/bin',
 '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip',
 '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6',
 '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload',
 '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages',
 '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/extensions',
 '/Users/mintou/.ipython']

In [3]: 

可以看下sys模块下面的几个路径,除了第一个,其他都是python包目录弱者site-packages(第三方)目录下了,那么第一个就是默认当前目录下查找,如果我们的两个文件在不同文件夹下,如何使用心里有点数了吧。

可以看到,两个py在同一py1下面,但是在各自不同的目录下面,看右边的代码,在数组index=1的位置插入额外的查询路径,这样,__import__动态导包才可以有效。同理,里面有多层目录,一样是在路径里面继续添加

 

第二种:__init__.py

首先如果一个文件夹可以通过import导入,这个文件夹必须变成包,就需要在文件夹目录下新建一个__init__.py的文件,当你再次import文件的时候,会执行__init__.py里面的代码,而import也生效了

mintoudeMacBook-Pro:py1 mintou$ tree
.
├── Pageges
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-36.pyc
│   │   └── sendMsg.cpython-36.pyc
│   ├── opps
│   │   ├── __init__.py
│   │   ├── __pycache__
│   │   │   ├── __init__.cpython-36.pyc
│   │   │   └── name.cpython-36.pyc
│   │   └── name.py
│   ├── reveiveMsg.py
│   └── sendMsg.py
└── test.py

 

还是看树形图吧,很清晰的可以看到test.py和Pageges文件夹是在同一层级的,那么test如何访问并使用Pageges里面的py呢?按第二种方法,我们要让Pageges变成包,因此在其目录下新建一个__init__.py,其中的代码如下

from .import sendMsg
from .import opps

第一句很容易理解,当外部import的时候都会执行__init__.py执行import,那么如果内部还有类似opps的文件夹,一样是和之前一样的处理,继续用__init__.py去执行深层次的import

type和isinstance

>>type('foo') == str
True
>>type(2.3) in (int,float)
True

既然有了type()来判断类型,为什么还有isinstance()呢?
一个明显的区别是在判断子类。
type()不会认为子类是一种父类类型。     ----> ismenberofclass
isinstance()会认为子类是一种父类类型。 ----> iskindofclass



class Foo(object):
    pass
 
class Bar(Foo):
    pass
 
print type(Foo()) == Foo
print type(Bar()) == Foo
print isinstance(Bar(),Foo)

True
False
True

 

 

定义类(init只负责初始化)

class 类名:
    方法列表
# 定义汽车类
class Car:

    def __init__(self, newWheelNum, newColor):
        self.wheelNum = newWheelNum
        self.color = newColor

    def move(self):
        print('车在跑,目标:夏威夷')

# 创建对象
BMW = Car(4, 'green')

print('车的颜色为:%s'%BMW.color)
print('车轮子数量为:%d'%BMW.wheelNum)
  • __init__()方法,在创建一个对象时默认被调用,不需要手动调用
  • __init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
  • __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去

 

class Car:

    def __init__(self, newWheelNum, newColor):
        self.wheelNum = newWheelNum
        self.color = newColor
    // 这里的 __str__就是OC里面的discription函数,用来打印类的信息,默认是打印地址的
    def __str__(self):
        msg = "嘿。。。我的颜色是" + self.color + "我有" + int(self.wheelNum) + "个轮胎..."
        return msg

    def move(self):
        print('车在跑,目标:夏威夷')


BMW = Car(4, "白色")
print(BMW)

 

保护对象的属性(getter setter)

如果有一个对象,当需要对其进行修改属性时,有2种方法

  • 对象名.属性名 = 数据 ---->直接修改
  • 对象名.方法名() ---->间接修改

为了更好的保存属性安全,即不能随意修改,一般的处理方式为

  • 将属性定义为私有属性
  • 添加一个可以调用的方法,供调用
class People(object):

    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name

    def setName(self, newName):
        if len(newName) >= 5:
            self.__name = newName
        else:
            print("error:名字长度需要大于或者等于5")

xiaoming = People("dongGe")

xiaoming.setName("wanger")
print(xiaoming.getName())

xiaoming.setName("lisi")
print(xiaoming.getName())
  • Python中没有像C++中public和private这些关键字来区别公有属性和私有属性
  • 它是以属性命名方式来区分,如果在属性名前面加了2个下划线'__',则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。

 

__call__()方法

实例出来的对象直接调用

class MKJ(object):
	"""docstring for MKJ"""
	def __call__(self):
		print("实例对象调用自己方法")

a = MKJ()
a()
	

__new__()
它是在创建实例的时候被调用(注意此处的"实例",我在这里并没有说"的实例",因为除了类,还有元类,元类创建实例的时候也会调用这个函数)

__call__()
官方定义:Called when the instance is "called" as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.__call__(arg1, arg2, ...).
它是在“实例被当成函数调用时”被调用。
举个例子,实例如果是"MKJ",那么,当你写下"MKJ()"的时候,该实例(即MKJ)的创建者(注意:此处提到的创建者既有可能是类,也有可能是元类)中的__call__()被调用。

如果这个实例是一个类,那么它的创建者就是一个元类,如果这个实例是一个对象,那么它的创建者就是一个类。

 

# __new__ 方法创建   类和对象
 
class SingleTon(object):
    __instance = None
 
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(SingleTon, cls).__new__(cls, *args, **kwargs)
        return cls.__instance
 
class Banaber(SingleTon):
    a = 1
 
 
 
# __call__ 方法创建  元类和类
 
class SingleTon(type):
    __intance = None
    def __call__(self, *args, **kwargs):
        if self.__intance is None:
            self.__intance = super().__call__(*args, **kwargs)
        return self.__intance
 
class Foo(metaclass=SingleTon):
    pass

__del__() 方法

 

创建对象后,python解释器默认调用__init__()方法;

当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法

OC 里面的dealloc方法

import time
class Animal(object):

    # 初始化方法
    # 创建完对象后会自动被调用
    def __init__(self, name):
        print('__init__方法被调用')
        self.__name = name


    # 析构方法
    # 当对象被删除时,会自动被调用
    def __del__(self):
        print("__del__方法被调用")
        print("%s对象马上被干掉了..."%self.__name)

# 创建对象
dog = Animal("哈皮狗")

# 删除对象
del dog


cat = Animal("波斯猫")
cat2 = cat
cat3 = cat

print("---马上 删除cat对象")
del cat
print("---马上 删除cat2对象")
del cat2
print("---马上 删除cat3对象")
del cat3

print("程序2秒钟后结束")
time.sleep(2)

就是OC的引用计数问题,一样的

 

单继承

class Animal(object):

    def __init__(self, name='动物', color='白色'):
        self.__name = name
        self.color = color

    def __test(self):
        print(self.__name)
        print(self.color)

    def test(self):
        print(self.__name)
        print(self.color)



class Dog(Animal):
    def dogTest1(self):
        #print(self.__name) #不能访问到父类的私有属性
        print(self.color)


    def dogTest2(self):
        #self.__test() #不能访问父类中的私有方法
        self.test()


A = Animal()
#print(A.__name) #程序出现异常,不能访问私有属性
print(A.color)
#A.__test() #程序出现异常,不能访问私有方法
A.test()

print("------分割线-----")

D = Dog(name = "小花狗", color = "黄色")
D.dogTest1()
D.dogTest2()
  • 私有的属性,不能通过对象直接访问,但是可以通过方法访问
  • 私有的方法,不能通过对象直接访问
  • 私有的属性、方法,不会被子类继承,也不能被访问
  • 一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用

 

多继承

#coding=utf-8
class base(object):
    def test(self):
        print('----base test----')
class A(base):
    def test(self):
        print('----A test----')

# 定义一个父类
class B(base):
    def test(self):
        print('----B test----')

# 定义一个子类,继承自A、B
class C(A,B):
    pass


obj_C = C()
obj_C.test()

print(C.__mro__) #可以查看C类的对象搜索方法时的先后顺序
# ----A test----
# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.base'>, <class 'object'>)

这里如果继承的方法都是一样的,可以子类优先,类.__mro__会打印出对象搜索的方法

 

#coding=utf-8
class Cat(object):
    def __init__(self,name):
        self.name = name
        self.color = 'yellow'


class Bosi(Cat):

    def __init__(self,name):
        # 调用父类的__init__方法1(python2)
        #Cat.__init__(self,name)
        # 调用父类的__init__方法2
        #super(Bosi,self).__init__(name)
        # 调用父类的__init__方法3
        super().__init__(name)

    def getName(self):
        return self.name

bosi = Bosi('xiaohua')

print(bosi.name)
print(bosi.color)

类属性和实例属性

class People(object):
    name = 'Tom'  #公有的类属性
    __age = 12     #私有的类属性

p = People()

print(p.name)           #正确
print(People.name)      #正确
print(p.__age)            #错误,不能在类外通过实例对象访问私有的类属性
print(People.__age)        #错误,不能在类外通过类对象访问私有的类属性

 

class People(object):
    address = '山东' #类属性
    def __init__(self):
        self.name = 'xiaowang' #实例属性
        self.age = 20 #实例属性

p = People()
p.age =12 #实例属性
print(p.address) #正确
print(p.name)    #正确
print(p.age)     #正确

print(People.address) #正确
print(People.name)    #错误
print(People.age)     #错误
class People(object):
    country = 'china' #类属性


print(People.country)
p = People()
print(p.country)
p.country = 'japan' 
print(p.country)      #实例属性会屏蔽掉同名的类属性
print(People.country)
del p.country    #删除实例属性
print(p.country)
  • 如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性

动态添加和__slots__

Python和其他动态语言一样,可以动态添加方法和属性,这一点很好理解验证,如果不让类能动态添加,就需要__slots属性了

class Person(object):
    __slots__ = ("name")
    def __init__(self):
        self.name = "aaa"

p =  Person()
p.age = 11
print(p.name)


# AttributeError: 'Person' object has no attribute 'age'
  • 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

 

类方法

是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。

 

class People(object):
    country = 'china'

    #类方法,用classmethod来进行修饰
    @classmethod
    def getCountry(cls):
        return cls.country

p = People()
print p.getCountry()    #可以用过实例对象引用
print People.getCountry()    #可以通过类对象引用

 

 

静态方法

 

 

class People(object):
    country = 'china'

    @staticmethod
    #静态方法
    def getCountry():
        return People.country


print People.getCountry()


从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用

 

工厂模式

在父类留下接口,在子类进行实现,因此各个子类表现出来的形式就不同

# 定义一个基本的4S店类
class CarStore(object):

    #仅仅是定义了有这个方法,并没有实现,具体功能,这个需要在子类中实现
    def createCar(self, typeName):
        pass

    def order(self, typeName):
        # 让工厂根据类型,生产一辆汽车
        self.car = self.createCar(typeName)
        self.car.move()
        self.car.stop()

# 定义一个北京现代4S店类
class XiandaiCarStore(CarStore):

    def createCar(self, typeName):
        self.carFactory = CarFactory()
        return self.carFactory.createCar(typeName)


# 定义伊兰特车类
class YilanteCar(object):

    # 定义车的方法
    def move(self):
        print("---车在移动---")

    def stop(self):
        print("---停车---")

# 定义索纳塔车类
class SuonataCar(object):

    # 定义车的方法
    def move(self):
        print("---车在移动---")

    def stop(self):
        print("---停车---")

# 定义一个生产汽车的工厂,让其根据具体得订单生产车
class CarFactory(object):

    def createCar(self,typeName):
        self.typeName = typeName
        if self.typeName == "伊兰特":
            self.car = YilanteCar()
        elif self.typeName == "索纳塔":
            self.car = SuonataCar()

        return self.car

suonata = XiandaiCarStore()
suonata.order("索纳塔")

 

__new__方法(只负责创建)

 

class A(object):
    def __init__(self):
        print("这是 init 方法")

    def __new__(cls):
        print("这是 new 方法")
        return object.__new__(cls)

A()
  • __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

  • __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

  • __init__有一个参数self,就是这个__new__返回的实例,__init____new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

  • 我们可以将类比作制造商,__new__方法就是前期的原材料购买环节,__init__方法就是在有原材料的基础上,加工,初始化商品环节

 

单例模式和只初始化一次

class Dog(object):
	"""docstring for Dog"""
	# def __init__(self, arg):
	# 	super(Dog, self).__init__()
	# 	self.arg = arg
	# 	
	instance = None
	firstInit = False

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


	def __init__(self,name,age):
		if Dog.firstInit == False:
			self.name = name
			self.age = age
			Dog.firstInit = True



dog1 = Dog("Tom",12)
print("%s + %s"%(id(dog1),dog1.name))
dog2 = Dog("Jerry",20)
print("%s + %s"%(id(dog2),dog2.name))


4328148104 + Tom
4328148104 + Tom


***Repl Closed***

 

 

系统第三方模块

如果装的是3.6,系统自带的是2.7

In [1]: import sys

In [2]: import os

In [3]: os.__file__
Out[3]: '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/os.py'

cd到路径下面就是系统默认自带的模块

安装第三方模块

sudo pip3 install pygame  在版本3路径下安装
sudo pip install pygame   在版本2路径下安装

两种检验方式是否安装成功

1.ipython3环境下

import 安装的模块,如果不报错,即可

2.在模块目录下查找

 

 

 

自建模块

    import math

    #这样会报错
    print sqrt(2)

    #这样才能正确输出结果
    print math.sqrt(2)
from 模块名 import 函数名1,函数名2....
  • 通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。也就是说假如模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。

  • 如果想一次性引入math中所有的东西,还可以通过from math import *来实现

Python的from语句让你从模块中导入一个指定的部分到当前命名空间中

语法如下:

    from modname import name1[, name2[, ... nameN]]

例如,要导入模块fib的fibonacci函数,使用如下语句:

    from fib import fibonacci

 

自建py模块的时候在其他文件import的时候,是会把模块的文件都执行一遍,例如你里面有print这些函数,也会在import的时候直接导入,因此在自建模块的里面,要用__name__来控制,自己运行__name__ = __main__ 如果是import运行,__name__就是test,因此可以根据这个字段判断是否在自测模块的时候打印,别人import的时候不执行打印

 

from xxx import *该方法导入的模块,如果模块里面有__all__参数,只能导入__all__ = ["函数1","类1"]列出的功能函数或者方法类

 

给程序传参

 

import sys

print(sys.argv)

在模块里面导入sys,然后终端执行

python3 xxx.py 参数一 参数二

模块里面就会接收到参数打印数组出来

 

列表生成式

Range在Python2中你要多少数组大小是直接给你的,因此你这样拿的话内存会可能爆炸

在Python3中就是动态给你的,而不是你Range多少都直接返回数组给你,你用到的时候才会给你数据分配空间

In [2]: a = [i for i in range(1,20)]

In [3]: a
Out[3]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
In [4]: a = [i for i in range(1,20) if i%2==0]

In [5]: a
Out[5]: [2, 4, 6, 8, 10, 12, 14, 16, 18]
In [6]: a = [(i,j) for i in range(1,3) for j in range(1,5)]

In [7]: a
Out[7]: [(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4)]

 

深Copy和浅Copy

deepcopy(递归深copy)

In [71]: a = [1,2,3]

In [72]: b = [4,5,6]

In [73]: c = [a,b]

In [74]: c
Out[74]: [[1, 2, 3], [4, 5, 6]]

In [75]: id(a)
Out[75]: 4406509384

In [76]: id(b)
Out[76]: 4404327368

In [77]: id(c[0])
Out[77]: 4406509384

In [78]: import copy

In [79]: d = copy.deepcopy(c)

In [80]: id(c)
Out[80]: 4404619720

In [81]: id(d)
Out[81]: 4407108424

In [82]: id(d[0])
Out[82]: 4404570248

In [83]: a.append(1000)

In [84]: a
Out[84]: [1, 2, 3, 1000]

In [85]: c[0]
Out[85]: [1, 2, 3, 1000]

In [86]: d[0]
Out[86]: [1, 2, 3]

In [87]: d
Out[87]: [[1, 2, 3], [4, 5, 6]]

上面的是递归深copy的操作,因此复制出来的是完全开辟另一个空间的全部内容复制,源对象的修改不会影响后面

 

copy(深copy)-->可变类型数组

In [89]: a = [1,2,3]

In [90]: b = [4,5,6]

In [91]: c = [a,b]

In [92]: c
Out[92]: [[1, 2, 3], [4, 5, 6]]

In [93]: d = copy.copy(c)

In [94]: a
Out[94]: [1, 2, 3]

In [95]: id(a)
Out[95]: 4407079304

In [96]: id(b)
Out[96]: 4407079816

In [97]: id(c)
Out[97]: 4404548808

In [98]: id(d)
Out[98]: 4407106120

In [99]: id(d[0])
Out[99]: 4407079304

In [100]: a.append(1000)

In [101]: a
Out[101]: [1, 2, 3, 1000]

In [102]: b
Out[102]: [4, 5, 6]

In [103]: c
Out[103]: [[1, 2, 3, 1000], [4, 5, 6]]

In [104]: d
Out[104]: [[1, 2, 3, 1000], [4, 5, 6]]

上面的内容可以看出,针对copy的操作,不会进行递归,只是对容器的copy,容器由于是可变类型数组,那么容器的地址c和d是不同的,但是容器内部的指针是不会进行深copy的,沿用之前的对象,源对象改变,能改变后面所有值

 

copy(深copy)-->不可变元祖

In [111]: a = [1,2,3]

In [112]: b = [4,5,6]

In [113]: c = (a,b)

In [114]: d = copy.copy(c)

In [115]: id(c)
Out[115]: 4406812232

In [116]: id(d)
Out[116]: 4406812232

In [117]: a.append(1000)

In [118]: a
Out[118]: [1, 2, 3, 1000]

In [119]: c
Out[119]: ([1, 2, 3, 1000], [4, 5, 6])

In [120]: d
Out[120]: ([1, 2, 3, 1000], [4, 5, 6])

针对不可变类型的copy就是浅copy,指针copy,其他和上面一样

 

OC里面的mutableCopy就是python里面的copy

NSMutableArray *a = [[NSMutableArray alloc] initWithArray:@[@"1",@"2",@"3"]];
    NSMutableArray *b = [[NSMutableArray alloc] initWithArray:@[@"222",@"333",@"444"]];
    
    NSArray *c = @[a,b];
    NSLog(@"%@ %p",c,c);
    NSLog(@"a的地址%p",a);
    NSLog(@"b的地址%p",b);
    
    NSArray *d = c.mutableCopy;
    
    NSLog(@"%@ %p",d,d);
    NSLog(@"a的地址%p",d[0]);
    NSLog(@"b的地址%p",d[1]);
    
    NSArray *e = c.copy;
    NSLog(@"%@ %p",e,e);
    NSLog(@"a的地址%p",e[0]);
    NSLog(@"b的地址%p",e[1]);
    
    [a addObject:@"10000"];
    NSLog(@"%@",c);
    NSLog(@"%@",d);
    NSLog(@"%@",e);
2018-06-19 10:24:28.929903+0800 Mutable[3954:70431] (
        (
        1,
        2,
        3
    ),
        (
        222,
        333,
        444
    )
) 0x600000026a40
2018-06-19 10:24:28.930026+0800 Mutable[3954:70431] a的地址0x6000000489d0
2018-06-19 10:24:28.930111+0800 Mutable[3954:70431] b的地址0x600000048340
2018-06-19 10:24:28.930215+0800 Mutable[3954:70431] (
        (
        1,
        2,
        3
    ),
        (
        222,
        333,
        444
    )
) 0x604000057af0
2018-06-19 10:24:28.930301+0800 Mutable[3954:70431] a的地址0x6000000489d0
2018-06-19 10:24:28.930390+0800 Mutable[3954:70431] b的地址0x600000048340
2018-06-19 10:24:28.930493+0800 Mutable[3954:70431] (
        (
        1,
        2,
        3
    ),
        (
        222,
        333,
        444
    )
) 0x600000026a40
2018-06-19 10:24:28.930573+0800 Mutable[3954:70431] a的地址0x6000000489d0
2018-06-19 10:24:28.930670+0800 Mutable[3954:70431] b的地址0x600000048340
2018-06-19 10:24:28.930770+0800 Mutable[3954:70431] (
        (
        1,
        2,
        3,
        10000
    ),
        (
        222,
        333,
        444
    )
)
2018-06-19 10:24:28.930876+0800 Mutable[3954:70431] (
        (
        1,
        2,
        3,
        10000
    ),
        (
        222,
        333,
        444
    )
)
2018-06-19 10:24:28.930994+0800 Mutable[3954:70431] (
        (
        1,
        2,
        3,
        10000
    ),
        (
        222,
        333,
        444
    )
)

下面的图是OC对象的copy操作,基本上操作和python一致

 

property

方案一

class Person(object):
	__slots__ = ["__age"]
	def __init__(self):
		super(Person, self).__init__()
		self.__age = 100

	def getAge(self):
		return self.__age

	def setAge(self,newAge):
		self.__age = newAge

	age = property(getAge,setAge)

person = Person()
print(person.age)
person.age = 200
print(person.age)

方案二

class Money(object):
	__slots__ = ["__num"]
	def __init__(self):
		super(Money, self).__init__()
		self.__num = 100000


	@property
	def num(self):
		return self.__num

	@num.setter
	def num(self,newNum):
		self.__num = newNum


money = Money()
print(money.num)
money.num = 100000000
print(money.num)

 

 

迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

 

以直接作用于 for 循环的数据类型有以下几种:

一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;

一类是 generator ,包括生成器和带 yield 的generator function。

这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。

 

 

 

是否可迭代

In [1]: from _collections_abc import Iterable

In [2]: isinstance(100,Iterable)
Out[2]: False

In [3]: isinstance("dsds",Iterable)
Out[3]: True

In [4]: isinstance({},Iterable)
Out[4]: True

In [5]: isinstance([1,2,3],Iterable)
Out[5]: True

In [6]: isinstance((x for x in range(10)),Iterable)
Out[6]: True

 

是否迭代器

In [1]: from  _collections_abc import  Iterator

In [2]: a = [1,2,3]

In [3]: isinstance(a,Iterator)
Out[3]: False

In [4]: isinstance({},Iterator)
Out[4]: False

In [5]: isinstance(x for x in range(10),Iterator)
  File "<ipython-input-5-0792a32e488c>", line 1
    isinstance(x for x in range(10),Iterator)
              ^
SyntaxError: Generator expression must be parenthesized if not sole argument


In [6]: isinstance((x for x in range(10)),Iterator)
Out[6]: True

字符串,list,字典都是可迭代

可迭代的东西不一定是可迭代对象,但是生成器必定是 (x for x in range(10))

可以用iter()来把 数组字典这些可迭代的换成可迭代的对象

这么做有个好处,数组字典一开始都已经开辟了控件,如果你需要用的时候再开辟,就可以转成迭代器来提高性能

  • 凡是可作用于 for 循环的对象都是 Iterable 类型;
  • 凡是可作用于 next() 函数的对象都是 Iterator 类型
  • 集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不过可以通过 iter() 函数获得一个 Iterator 对象。

 

闭包

内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。

 

def test1(number1):
	def test2(number2):
		print(number1 + number2)
	return test2


a = test1(100)

a(200)
a(1000)
a(1)

300
1100
101
***Repl Closed***

 

按照iOS的的思路去理解,其实关于对象的内存问题,基本都是一样的,有变量强引用的情况下就不会被回收,那么就很好理解闭包了

 

在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,那么将这个函数和用到一些变量称之为闭包。

可以看到定一个一个test1函数,开辟了内存,有个变量number1,然后函数内部又定义了一个test2函数,由test1函数返回给外部变量a绑定指着,那么外部每次调用a,也就是调用了test1函数内部的test2函数,每次调用都会去拿最早之前传进去的numer1的值,为什么调用完函数之后,test1没有被回收,因为其内部还有个函数,被外部的变量a强引用着,因此只要a不会销毁,test1就会一直存在,number1也就一直存在,好处是调用的时候直接拿,坏处是一直占用着内存

 

1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

 

 

装饰器

# 语法糖
def updateTaskWithAuth(func):
	def authorlizations():
		print("验证权限")
		func()
	return authorlizations

@updateTaskWithAuth
def func2():
	print("业务二")

@updateTaskWithAuth
def func1():
	print("业务一")


# 原始调用  一
# func1()
# func2()

# 新增鉴权  二
# func1 = updateTaskWithAuth(func1)
# func1()

# func2 = updateTaskWithAuth(func2)
# func2()


# 依旧原始调用
# 在闭包中,如果不改变原始调用,新增功能,可以另外写一个方法,然后通过传入原先的业务功能例如func1,最终改变func1默认函数指针的指向
# 改为指向闭包内部返回的函数即可,如果用语法糖就需要在业务函数外部加上@闭包函数名,然后原样调用,就是直接赋值的效果
func1()
func2()
  1. 引入日志
  2. 函数执行时间统计
  3. 执行函数前预备处理
  4. 执行函数后清理功能
  5. 权限校验等场景
  6. 缓存

其实可以看到,无非就是在调用原函数的时候,在外部不变的情况下,类似iOS的中的RuntimeHook函数,只是实现稍微有点不同而已,Python是通过@语法糖里面的闭包来实现,iOS是Runtime底层交换方法来实现,再不改原先逻辑的情况下,在方法之前嵌入自己的逻辑,例如日志,统计,预处理,清理,校验等场景

这里明白@updateTaskWithAuth就已经会执行一段代码例如

func1 = updateTaskWithAuth(func1)改变了原先func1的指向罢了,但是这句代码是会执行的,当从上到下遇到@装饰时时会先执行的

 

类似例如DJango中的request请求参数传递

def updateTaskWithAuth(func):
    def authorlizations(para,*args, **kwargs):
        print('%s--%s-%s'%(para,args,kwargs))
        # print("验证权限%s"%args)
        func(para)

    return authorlizations


@updateTaskWithAuth
def func2(para):
    print("业务二")


@updateTaskWithAuth
def func1():
    print('业务二')

func2({"a":111}, '参数二', a=1)


{'a': 111}--('参数二',)-{'a': 1}
业务二

调用函数的所有参数都会被装饰器带走传递给闭包函数,最终通过func回调

 

多个装饰器

def w1(func):
	print("装饰1")
	def inner():
		print("插入功能1")
		func()
	return inner


def w2(func):
	print("装饰2")
	def inner():
		print("插入功能2")
		func()
	return inner

@w1
@w2
def func1():
	print("业务1")


func1()

装饰2
装饰1
插入功能1
插入功能2
业务1

***Repl Closed***

 

根据图片分析下,首先编译器遇到@w1和@w2,这里是会有代码执行的,上面有介绍,就不展开了,但是前提是装饰器的下一句代码是方法函数,才会装饰,因此先跳过@w1,然后@w2就会对func函数进行装饰,因此先执行装饰2,然后返回的值就是inner函数,再执行装饰1,执行的时候就是先执行装饰1里面的inner函数,然后在执行装饰2里面的inner函数,好比一个东西,包装的时候由内到外,执行的时候由外到内,这就是多层装饰的逻辑

 

 

万能通用装饰器(iOS中的MethodSwizzle效果)

def func(funcTemp):
	print("装饰函数----开始装饰")
	def innderFunc(*args,**kwargs):
		print("装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。")
		return funcTemp(*args,**kwargs)
	return innderFunc

@func
def func1():
	print("无参数,无返回值")
@func
def func2():
	print("无参数,有返回值")
	return "宓珂璟"
@func
def func3(a):
	print("有一个参数 %d"%(a))
@func
def func4(a,b,c):
	print("有多个参数 %d,%d,%d"%(a,b,c))
	return a + b + c


print(func1())
print(func2())
print(func3(100))
print(func4(100,200,300))


装饰函数----开始装饰
装饰函数----开始装饰
装饰函数----开始装饰
装饰函数----开始装饰
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
无参数,无返回值
None
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
无参数,有返回值
宓珂璟
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
有一个参数 100
None
装饰函数----执行装饰实际代码例如插入,验证,统计等。。。。。。
有多个参数 100,200,300
600

***Repl Closed***

这就是一个通用装饰器

如果装饰器要带参数,外面再包一层即可

from time import ctime, sleep

def timefun_arg(pre="hello"):
    def timefun(func):
        def wrappedfunc():
            print("%s called at %s %s"%(func.__name__, ctime(), pre))
            return func()
        return wrappedfunc
    return timefun

@timefun_arg("hehe")
def foo():
    print("I am foo")

@timefun_arg("python")
def too():
    print("I am too")

foo()
sleep(2)
foo()

too()
sleep(2)
too()
foo()==timefun_arg("hehe")(foo)()

类装饰器

class Test(object):
    def __init__(self, func):
        print("---初始化---")
        print("func name is %s"%func.__name__)
        self.__func = func
    def __call__(self):
        print("---装饰器中的功能---")
        self.__func()
#说明:
#1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
#    并且会把test这个函数名当做参数传递到__init__方法中
#    即在__init__方法中的func变量指向了test函数体
#
#2. test函数相当于指向了用Test创建出来的实例对象
#
#3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#
#4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
#    所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体
@Test
def test():
    print("----test---")
test()

 

作用域

globals() 打印出所有全局变量

locals() 打印出函数局部变量里面的所有局部变量

dir(__builtin__) 查看内建模块函数

Python 使用 LEGB 的顺序来查找一个符号对应的对象

locals -> enclosing function -> globals -> builtins
  • locals,当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量
  • enclosing,外部嵌套函数的命名空间(闭包中常见)

 

  • globals,全局变量,函数定义所在模块的命名空间
  • builtins,内建模块的命名空间。

 

 

生成器

在Python中,这种一边循环一边计算的机制,称为生成器:generator

方法一 [] ---> ()

In [15]: L = [ x*2 for x in range(5)]

In [16]: L
Out[16]: [0, 2, 4, 6, 8]

In [17]: G = ( x*2 for x in range(5))

In [18]: G
Out[18]: <generator object <genexpr> at 0x7f626c132db0>

In [19]:

方法二(yield)

def fib(times):
	a,b = 0,1
	n = 0
	print("----1-----")
	while n<times:
		print("----2-----")
		yield b
		print("----3-----")
		a,b = b,a+b
		n += 1
	return "宓珂璟"




def hehe(gerner):
	for x in gerner:
		print(x)


F = fib(5)
hehe(F)

----1-----
----2-----
1
----3-----
----2-----
1
----3-----
----2-----
2
----3-----
----2-----
3
----3-----
----2-----
5
----3-----


***Repl Closed***

在循环过程中就会不断调用yield,并且不断中断,只有当我们调用next的时候才会继续执行,遇到yield继续中断,或者直接遍历生成器也能不断调用执行yield

 

send可以给yield赋值的变量传值

def test():
    i = 0
    while i < 6:
        temp = yield i
        print(temp)
        i += 1


t = test()

print(next(t))
print(next(t))
t.send("宓珂璟")
print(next(t))
print(next(t))
print(next(t))



# 0
# None
# 1
# 宓珂璟
# None
# 3
# None
# 4
# None
# 5

# ***Repl Closed***

生成器的特点:

  1. 节约内存
  2. 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

 

对比看下简单的迭代器和生成器,就是把原先数组需要开辟的空间,通过一边使用一边开辟,节约内存

In [2]: def test():
   ...:     n = 0
   ...:     while(n<6):
   ...:         yield n
   ...:         n += 1
   ...:        

In [3]: a = test()

In [4]: a
Out[4]: <generator object test at 0x106894728>

In [5]: b = [1,2,3]

In [6]: b
Out[6]: [1, 2, 3]

In [7]: b = iter(b)

In [8]: b
Out[8]: <list_iterator at 0x1068ab9b0>

In [9]: c = (x for x in range(10))

In [10]: c
Out[10]: <generator object <genexpr> at 0x106894fc0>

 

元类

元类就是用来创建类的“东西”

 

和OC一样,class也是一种对象,他的父类就是元类

>>> print type(1) #数值的类型
<type 'int'>
>>> print type("1") #字符串的类型
<type 'str'>
>>> print type(ObjectCreator()) #实例对象的类型
<class '__main__.ObjectCreator'>
>>> print type(ObjectCreator) #类的类型
<type 'type'>

类的type是 'type'

In [16]: class MK(object):
    ...:     def __init__(self):
    ...:           self.name = None
    ...:           

In [17]: type(MK)
Out[17]: type

In [18]: a = MK()

In [19]: a
Out[19]: <__main__.MK at 0x1069d5eb8>

In [20]: type(a)
Out[20]: __main__.MK

In [21]: type(MK)
Out[21]: type

In [22]: clear


In [23]: myDogClass = type("MyDog",(),{})

In [24]: myDogClass
Out[24]: __main__.MyDog

In [25]: MK()
Out[25]: <__main__.MK at 0x106971518>
 
In [27]: print(MK)
<class '__main__.MK'>

In [28]: print(myDogClass)
<class '__main__.MyDog'>

In [29]: print(myDogClass())
<__main__.MyDog object at 0x106971860>

type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值)以及静态或者类方法)

元类就是类的类

MyClass = MetaClass() #使用元类创建出一个对象,这个对象称为“类”
MyObject = MyClass() #使用“类”来创建出实例对象

你也可以看到如下代码

MyClass = type('MyClass', (), {})

这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类

Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type

type是Python里面的内建元类,不过你也可以自己创建

class upperSuperClass(type):

    def __new__(cls, className, upperSuperClassName, classAttr):
        print('21321312')
        newDict = {}
        for key, value in classAttr.items():
            if not key.startswith("__"):
                newDict[key.upper()] = value
        return super(upperSuperClass, cls).__new__(cls, className, upperSuperClassName, newDict)


class Foo(object, metaclass=upperSuperClass):
    bar = "pip"


f = Foo()
print(f.BAR))

Python做了如下的操作:

  1. Foo中有__metaclass__这个属性吗?如果是,Python会通过__metaclass__创建一个名字为Foo的类(对象)
  2. 如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。
  3. 如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。
  4. 如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。

元类的作用

  1. 拦截类的创建
  2. 修改类
  3. 返回修改之后的类

 

 

内建属性(dir)

In [2]: class Person(object):
   ...:     pass
   ...: 
   ...: 

In [3]: dir(Person)
Out[3]: 
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__']
常用专有属性 说明 触发方式
__init__ 构造初始化函数 创建实例后,赋值时使用,在__new__
__new__ 生成实例所需属性 创建实例时
__class__ 实例所在的类 实例.__class__
__str__ 实例字符串表示,可读性 print(类实例),如没实现,使用repr结果
__repr__ 实例字符串表示,准确性 类实例 回车 或者 print(repr(类实例))
__del__ 析构 del删除实例
__dict__ 实例自定义属性 vars(实例.__dict__)
__doc__ 类文档,子类不继承 help(类或实例)
__getattribute__ 属性访问拦截器 访问实例属性时
__bases__ 类的所有父类构成元素 类名.__bases__
class Itcast(object):
    def __init__(self,subject1):
        self.subject1 = subject1
        self.subject2 = 'cpp'

    #属性访问时拦截器,打log
    def __getattribute__(self,obj):
        if obj == 'subject1':
            print('log subject1')
            return 'redirect python'
        else:   #测试时注释掉这2行,将找不到subject2
            return object.__getattribute__(self,obj)

    def show(self):
        print('this is Itcast')

s = Itcast("python")
print(s.subject1)
print(s.subject2)

__getattribute__例子中后面跟的参数self是自身,obj就是对应的属性字符串名字,属性的访问是会先根据对象的__dict__,然后在类的__dict__中查找,如果没有才会进入上面的__getattribute__方法里面

 

log subject1
redirect python
cpp

__getattribute__的坑

class Person(object):
        def __getattribute__(self,obj):
            print("---test---")
            if obj.startswith("a"):
                return "hahha"
            else:
                return self.test


        def test(self):
            print("heihei")


    t.Person()

    t.a #返回hahha

    t.b #会让程序死掉
        #原因是:当t.b执行时,会调用Person类中定义的__getattribute__方法,但是在这个方法的执行过程中
        #if条件不满足,所以 程序执行else里面的代码,即return self.test  问题就在这,因为return 需要把
        #self.test的值返回,那么首先要获取self.test的值,因为self此时就是t这个对象,所以self.test就是
        #t.test 此时要获取t这个对象的test属性,那么就会跳转到__getattribute__方法去执行,即此时产
        #生了递归调用,由于这个递归过程中 没有判断什么时候推出,所以这个程序会永无休止的运行下去,又因为
        #每次调用函数,就需要保存一些数据,那么随着调用的次数越来越多,最终内存吃光,所以程序 崩溃
        #
        # 注意:以后不要在__getattribute__方法中调用self.xxxx

__getattribute__ 是属性拦截方法,如果重写,就不会按照先找对象的__dict__,再找类的,__getattr__则是之前的方法找不到才会最终调用的方法。一般不要重写__getattribute__,避免修改处上面的Bug,直接重写__getattr__就好

 

 

 

内建函数

map函数

 

map函数会根据提供的函数对指定序列做映射

    map(...)
        map(function, sequence[, sequence, ...]) -> list
  • function:是一个函数
  • sequence:是一个或多个序列,取决于function需要几个参数
  • 返回值是一个list

参数序列中的每一个元素分别调用function函数,返回包含每次function函数返回值的list。

#函数需要一个参数
map(lambda x: x*x, [1, 2, 3])
#结果为:[1, 4, 9]

#函数需要两个参数
map(lambda x, y: x+y, [1, 2, 3], [4, 5, 6])
#结果为:[5, 7, 9]


def f1( x, y ):  
    return (x,y)

l1 = [ 0, 1, 2, 3, 4, 5, 6 ]  
l2 = [ 'Sun', 'M', 'T', 'W', 'T', 'F', 'S' ]
l3 = map( f1, l1, l2 ) 
print(list(l3))
#结果为:[(0, 'Sun'), (1, 'M'), (2, 'T'), (3, 'W'), (4, 'T'), (5, 'F'), (6, 'S')]

filter函数

 

filter函数会对指定序列执行过滤操作

filter(...)
    filter(function or None, sequence) -> list, tuple, or string

    Return those items of sequence for which function(item) is true.  If
    function is None, return the items that are true.  If sequence is a tuple
    or string, return the same type, else return a list.
  • function:接受一个参数,返回布尔值True或False
  • sequence:序列可以是str,tuple,list

filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素。

返回值的类

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/deft_mkjing/article/details/80675517
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-02-13 15:36:33
  • 阅读 ( 1064 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢