python全栈开发

re

import random
print(random.random())#0-1的浮点型-----0.9428457286935451
# print(random.randint(1,3))#产生随机整数------[1,2,3]-都是闭区间
print(random.randrange(1,3))#[1,3)--左闭右开
# print(random.choice([1,2,[1,3]]))#方法返回一个列表,元组或字符串的随机项,[1,3]是整体返回[1,3]
# print(random.choice([1,2,[1,3]]))#choice表示是从列表中随机产生的
# print(random.sample([1,2,[1,3]],3))#[2, [1, 3]]返回的是列表,其中第二个参数可以写3
print(random.uniform(1,4))#选择任意范围的浮点数
#
#
# x=[1,3,4,5,6]
# random.shuffle(x)#打乱列表的次序
# print(x) #[4, 5, 1, 3, 6]

import re
l=re.findall('(abc)+','abcabcabc')
print(l)#['abc']

time

import time
print(time.time()) #1533188050.0860598
print(time.localtime())#结构化时间,返回时间对象,表示当前的时间
#time.struct_time(tm_year=2018, tm_mon=8, tm_mday=2,tm_hour=13, tm_min=38, tm_sec=3, tm_wday=3, tm_yday=214, tm_isdst=0)
print(time.localtime().tm_year)#返回其中一个属性2018
print(time.gmtime())#世界标准时间
# time.struct_time(tm_year=2018, tm_mon=8, tm_mday=2,tm_hour=5, tm_min=46, tm_sec=57, tm_wday=3, tm_yday=214, tm_isdst=0)
print(time.mktime(time.localtime()))#结构化时间转化为时间戳----1533189639.0

# -------结构化时间转化为字符串时间------
# time.strftime(a,b)a-最后展示的样式,b-结构化时间
# %Y-年;%m-月;%d-日 ; %X-展示所有时分秒;%M-秒,%h-时
print(time.strftime('%Y-%m-%d-%X',time.localtime()))#2018-08-02-14:22:37

#-----将字符串时间转为结构化时间
print(time.strptime('2018-08-02-14:22:19','%Y-%m-%d-%X'))
#time.struct_time(tm_year=2018, tm_mon=8, tm_mday=2, tm_hour=14, tm_min=22, tm_sec=19, tm_wday=3, tm_yday=214, tm_isdst=-1)

print(time.asctime())
#Python time asctime() 函数接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"(2008年12月11日 周二18时07分14秒)的24个字符的字符串。

print(time.ctime())
# Python time ctime() 函数把一个时间戳(按秒计算的浮点数)转化为time.asctime()的形式。

模块与包

https://www.cnblogs.com/cicaday/p/python-decorator.html

self

  • 是对象/属性/实例的集合
  • 表示类实例本身

组合与继承

  • 组合:大类中包含小类,组合类之间没有共同点,但是有联系,比如学校和老师,这两个类。
  • 继承:两个类有相似之处,比如动物和猫,继承有单继承和多继承
  • 子类继承父类的所有属性,当子类的属性与父类中的属性相同的时候,会优先调用子类中的属性,并不会覆盖父类中的属性,也不会改变父类中的属性。

组合

"""组合,继承"""
'''组合'''
class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr
class Course:
    def __init__(self,courseName,coursePrice):
        self.courseName=courseName
        self.coursePrice=coursePrice
    def showCourse(self):
        print("课程%s,课时费是%s"%(self.courseName,self.coursePrice))

class Teacher:
    def __init__(self,name,age,courseName,coursePrice):
        self.name=name
        self.age=age
        self.course=Course(courseName,coursePrice)            #组合的方式,这种方式其实就是传对象
    # def teaCourse(self):                                  
    #     print('老师%s,课程'%(self.name,self.course.showCourse()))  #这种调用方式不行,因为self.course.showCourse()其实是一个对象不是一个字符串

 #展示老师授课
t=Teacher('gx','23','linux','100')
t.course.showCourse()


接口继承与归一化设计

  • 代码之间应该解耦,使用接口继承方式
  • 抽象类只能被继承,不能被实例化
  • @abstractmethod
  • 调用的库abc
  • 注意点:父类的写法metaclass=abc.ABCMeta
import abc
class All(metaclass=abc.ABCMeta): #注意此处继承括号中的写法
    @abc.abstractmethod
    def read(self):
        pass
    @abc.abstractmethod
    def write(self):
         pass
class Disk(All):               #其实磁盘和cdrom的读写方式不一样,所以直接继承方式不合适
    def read(self):            # 归一化思想(接口继承)规范后面继承的子类中的方法,当写了@abstractmethod后子类中就必须摇有该方法
        print("disk 读方法")  #父类中可以不实现具体方法
    # def write(self):
    #     pass


class Cdrom(All):
    def read(self):
        print("Cdrom读方法")

    def write(self):
        pass

d=Disk()
d.read()
d.write()                       
#子类中如果没有实现父类中规定的方法,报错如下
# Traceback (most recent call last):
#   File "D:/project/PycharmProjects/pyTest/day25/归一化设计.py", line 23, in <module>
#     d=Disk()
# TypeError: Can't instantiate abstract class Disk with abstract methods write

继承顺序之mro先行顺序

  • py3新式类:广度有先和深度优先
  • py2有经典类,经典类就是子类或者父类没有击沉object类

在子类中调用父类的方法

class person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def walk(self):
        print("%s在走路"%self.name)

'''子类调用父类中的方法
方法1:父类名.父类方法(self,父类方法参数1,父类方法参数2,...)
方法2:super().父类方法(父类方法参数1,父类方法参数2,...)
注意:方法2括号中不需要写self
方法2的优势在于,当父类的名字改变时,不需要改变子类中对父类方法调用的父类名字
'''
class son(person):
# 方法1
#     def __init__(self,name,age,weight):
#         person.__init__(self,name,age)
#         self.weight=weight
#     def walk(self):
#         person.walk(self)


#方法2
     def __init__(self,name,age,weigth):
         super().__init__(name,age)
         self.weigth=weigth
     def walk(self):
         super().walk()


s=son("son","23",60)
s.walk()

多态

  • 多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类
  • 不同的对象调用同一个方法,结果是不同,需要注意的是,多态的基础是继承.

封装

封装的三个层次
  • 层次1:封装就是把东西都装到一个麻袋中
  • 层次2:限制外部对内部的访问,使用_paraname(一个下划线+属性的名字),__paraname(两个下划线+属性的名字),这种命名方式只是一种约定,python内部并没有严格限制,只要是上面的写法,外部就不可以访问
  • 层次3:第三个层次也是最重要的层次,私有属性的诞生,并不是说程序员想把那些不想给外部访问私有化就可以了,而是说程序员经过深思熟虑在类中切开一个合适的接口,这个接口是小的,如果随便把私有化属性,到后面如果其他类想要调用这个属性,只能通过重新定义一个方法,然后再返回参数的方式取数据.
演示单/双下划线的约定规则

“单下划线” 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;“双下划线” 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据

  • 私有变量轧压(Private name mangling)::在变量前端插入类名,再在前端加入一个下划线字符。
  • 注意:
  • 一是因为轧压会使标识符变长,当超过255的时候,Python会切断,要注意因此引起的命名冲突。
  • 二是当类名全部以下划线命名的时候,Python就不再执行轧压。
'''encapsulation:封装'''
class person:
    _name='gx'    #约定格式1
    __age=18
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def walk(self):
        print('[%s]正在走路'%self.name)

p=person("小明",54)
print(p._name)   #还是可以调用的  ------>gx
# print(p.__age)
#直接引用报错
#  File "C:/tools/pycharm/subject/encapsulation.py", line 13, in <module>
#     print(p.__age)
# AttributeError: 'person' object has no attribute '__age'
#这是因为python把双下划线__age,重定义为单下划线  _类名.__age
print(p._person__age)  #结果:18
演示外部调用单/双下划线的规则
一个有趣的代码段
'''python命名机制'''
class A(object):
    def __init__(self):
        self.__private()
        self.public()

    def __private(self):
        print('A.__private()')

    def public(self):
        print( 'A.public()')
class B(A):
    def __private(self):
        print('B.__private()')
    def public(self):
        print('B.public()')


b = B()

#结果
# A.__private()
# B.public()

反射

反射的四个函数
  • getattr
  • hasattr
  • delattr
  • setattr
  • 反射的作用是可以先定义方法的接口,其他程序可以先判断有没有方法,然后等方法实现了再使用.
class FOO(object):
    def __init__(self,name):
        self.name=name
    def func(self):
        return "ok"

f=FOO('小明')
print(f.__dict__)#数据属性 {'name': '小明'}

print(getattr(f, 'func','not found')) #获取的是个对象地址,<bound method FOO.func of <__main__.FOO object at 0x000001D9EC3025C0>>
print(getattr(f, 'name'))
# getattr(instance,属性,默认值)
# 逻辑:如果instance有属性方法,则打印方法的地址,有属性值打印属性值,
# 否则看三个参数,有第三个参数,则打印,没有则打印default

print(hasattr(f,'func')) #true
print(hasattr(f,'func1'))#false
# hasattr 是通过调用getattr,看getattr是否返回异常,如果返回异常为false,否则为true

setattr(f,'func','new')
print(f.__dict__)#{'name': '小明', 'func': 'new'}
# setattr(object,name,value),给属性name,新增属性value
# setattr(f,'func','new')相当于f.func=new

delattr(f,'name')
print(f.__dict__)#{'func': 'new'}
# delattr(object,name)删除object的属性name
# delattr(object,name)相当于del object.name

属性,静态方法,类方法

@property-相当于把函数属性变为数据属性
调用时候可以使用instance.方法名

@staticmethod-类的工具包,与类和实例都不绑定的方法
有了这个方法,从此类可以直接通过点运算调用静态方法,不需要与具体的实例绑定,方法的括号中不要self,cls,这种绑定关系

@classmethod-类方法
函数不需要传入实例,不要self,只和类进行捆绑,传入cls,
这个方法使用的场景是,当前方法与实例没有,与类有密切的关系
虽然类和实例都可以通过点运算调用静态方法,但是不建议实例调用静态方法。

import time
class School:
    x=123
    def __init__(self,name,addr,length):#分层思想
        self.name=name
        self.addr=addr
        self.length=length
    def  show_info(self):        #实例方法,使用self与实例绑定
        print("学校名字:%s,地址:%s"%(self.name,self.addr))
    @property                    #静态属性
    def areas(self):
        print("学校的长度%d"%self.length)

    @staticmethod                #静态方法-类的工具包,不需要与类和实例绑定
    def showtime():
        print(time.localtime())
    @classmethod                 #类方法-虽然实例也可以调用但是一般给类使用
    def value(cls):
        print(cls.x)            #使用类属性,就是最上面只和类有关系的属性
s=School("湖南工程学院","湖南",11111100)
s.show_info()

print(s.__dict__)#{'name': '湖南工程学院', 'addr': '湖南'}
                  # 打印实力属性

#类属性的调用 ------->>实例.类属性名字
s.areas         #实例可以直接像调用实例属性调用方法,不需要加括号运算

#静态方法调用 --------->>实例/类.静态方法
School.showtime()
s.showtime()


# 类方法的调用----------->类.类方法
School.value()

动态导入模块

  • 方法1:import(moduleName)
  • 方法2:模块importlib(官网推荐)

目录结构

subject
----->m
----------->t.py
----->atest.py
# m与atest.py是一个目录级别,t.py是m的下面的
  • 案例1:普通的import导入不能导入__func(),前面有下划线的方法

t.py

def test1():
    print('我是test1方法')
def _test2():
    print('我是test2方法')

atest.py

from m.t import *
test1()
_test2()

atest.py运行后结果如下:
就是说from /import这种方式导入的时候

Traceback (most recent call last):
我是test1方法
  File "C:/tools/pycharm/subject/atest.py", line 6, in <module>
    _test2()
NameError: name '_test2' is not defined

但是使用动态导入就不会有上面的错误

方式1:import(moduleName)

atest.py

# import importlib
# importlib.import_module(encapsulation)

spacename=__import__('m.t')
#动态导入的方式支持字符串导入

print(spacename)
# <module 'm' from 'C:\\tools\\pycharm\\subject\\m\\__init__.py'>
# 也就是说内部动态导入方式,即使后面有很多点,也只会导入第一个命名的大空间,所以只有m

spacename.t.test1()
spacename.t._test2()
# 我是test1方法
# 我是_test2方法,私有方法能够被导出
方式2:使用模块importlib
import  importlib
spacename=importlib.import_module('m.t')
print(spacename)
# <module 'm.t' from 'C:\\tools\\pycharm\\subject\\m\\t.py'>
# 使用模块importlib ,会导入所有的命名空间

spacename.test1()
spacename._test2()
# 我是test1方法
# 我是_test2方法

类的内置attr属性

  • getattr:属性不存在的触发,搜索属性的时候会触发执行
  • setattr:新增,修改属性的时候触发
  • delattr:删除的时候优先触发
    dir(object)与__dict__都可以查看属性

继承的方式完成包装

'''
包装/授权
1.包装:主要是在已有的功能的基础上,对已有的功能进行设置(对功能进行增删改查)
2.授权:加工的部分是已有功能没有的
'''

# 案例1:使用继承的方式完成包装---让append只能添加字符串,其他类型的不能添加
class newList(list):

    def append(self, obj):#方法的名字一样,是对继承方法的重写,增加了它的功能
        if type(obj) is str:
            #list.append(self,obj)#第一种父类调用的方法,一定要记得写self
            super().append(obj)   #第二种父类调用的方法
        else:
            print('非字符串类型不能添加')
        
        
n=newList()
n.append(123)
n.append('123')
print(n.__dict__)

组合的方式完成授权

授权:更新的功能使用自己定义的类中的

# 案例二:使用组合的方式完成授权
class  Cpu:#定义一个Cpu类
    def __init__(self,cpubrand,cpuprice):#cpu的属性有品牌、价格等
        self.cpubrand=cpubrand
        self.cpuprice=cpuprice


    def calc(self):
        print("CPU是电脑的大脑,你能进行大量的计算")


class Mainboard:#定义一个主板类
    def __init__(self,mbprice,mbsize):#主板的属性有价格和型号
        self.mbprice=mbprice
        self.mbsize=mbsize

    def connect(self):
        print("主板类似于人类的脊椎神经,它能够将显卡、声卡等设备联系起来")

class Computer:#定义一个电脑类
    def __init__(self,comprice,combrand,cpuprice,cpubrand,mbprice,mbsize):
        self.comprice=comprice
        self.combrand=combrand
        self.cpu=Cpu(cpuprice,cpubrand)
        self.mainboard=Mainboard(mbprice,mbsize)
    def play_game(self):
        print("嘿嘿,我们可以用电脑来玩游戏啊")
    def __getattr__(self, item):
        return getattr(self.cpu,item)
    
com=Computer(6999,"三星",2345,"intel",1800,'B53')
com.calc()

isinstance(obj,cls) && issubclass(sub,super)

'''issubclass ,isinstance'''
class FOO:
    pass
class Bar(FOO):
    pass 
f=FOO()
print(isinstance(f,FOO))  # True
#参数1:对象,
#参数2:类

getattribute:当属性存在或者没有都会执行,在__getattr__之前执行

getitem,setitem,delitem

  • 当我们对类的属性item进行下标的操作时,首先会被__getitem__()、setitem()、delitem()拦截,从而进行我们在方法中设定的操作,如赋值,修改内容,删除内容等等。
  • 区分:使用点的方式对属性进行操作就是与__xxxattr__有关,
  • 使用中括号的方式就是与__xxxitem__相关
'''计算差值'''
## item系类的三种方法,是对属性赋值的时候会触发__setitem__所以不能再使用x.name=123这样的方式
class ArithmeticSequence(object):
    def __init__(self,start=0,step=1):
        print('call function __init__')
        self.start=start
        self.step=step
        self.mydata={}

    def __getitem__(self, item):
        print('call function __getitem')
        try:
          return self.mydata[item]           #如果存在直接返回
        except :
            return  self.start+(item-1)*self.step #不存在就计算一下放回

    def __setitem__(self, key, value):
        print('call function __setitem__')
        self.mydata[key]=value

    def  __delattr__(self, item):
        print('call function __delitem__')
        del self.mydata[item]
a=ArithmeticSequence(1,2)
print(a[5])
a[6]=10          #使用类似字段的方式进行赋值
print(a.__dict__)   #  {'start': 1, 'step': 2, 'mydata': {6: 10}}
print(a[6])

区分

* __get__
* __getattr__
* __getattribute__
* __getitem__:使用字典方式访问属性时调用

案例1:getattrbute>getattr

class Foo:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __get__(self):
        print('__get__方法')
    def __getattr__(self, item):
        print('__getattr__方法')
    def __getattribute__(self, item):
        print('__getattribute__方法')
f=Foo('gx',25)
f.name
#__getattribute__方法
#所以获取方法的时候,__getattribute__>__getattr__

案例2:什么时候触发__getattr__

class Foo:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __get__(self):
        print('__get__方法')
    def __getattr__(self, item):
            print('__getattr__方法')
f=Foo('gx',25)
f.sss# 点运算找不到的时候会触发__getattr__

str__与__repr

案例1:字符串的方法重写使用__str__

'''str'''
class  FOO:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return '名字是%s,年龄是%s' %(self.name,self.age)
    
f=FOO('小明',18)
print(f)#未重定义__str__ 方法----><__main__.FOO object at 0x000001E1636983C8>
print(f) #重定义__str__ 方法--->名字是小明,年龄是18
print('小明')

案例2:__repr__方法是在解释器中执行的

在解释器中执行的效果

>>> class  FOO:
...     def __init__(self,name,age):
...         self.name=name
...         self.age=age
...     def __repr__(self):
...         return 'name is %s,age is %s' %(self.name,self.age)
...
>>> f=FOO('gx',18)
>>> f
name is gx,age is 18

在非解释器中执行的效果

'''str'''
class  FOO:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __repr__(self):
        return '名字是%s,年龄是%s' %(self.name,self.age)

f=FOO('小明',18)
print(f)#如果没有__str__,则会选择使用__repr__作为备用方案`

案例3:__str__不可以返回非字符串类型,否则报错

'''str'''
class  FOO:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return 1  #如果重写__str__方法,但是返回值不是字符串,则报错


f=FOO('小明',18)
print(f)#TypeError: __str__ returned non-string (type int)


案例4:当两个方法都存在的时候,会执行__str__方法,当没有这个方法会使用__repr__方法做未替代方案

class  FOO:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return '我是str方法'
    def __repr__(self):
        return 'name is %s,age is %s' %(self.name,self.age)

f=FOO('小明',18)
print(f)#我是str方法

format

__format__是format格式化调用的方法,通过对这个方法的重写,可以自定义format

'''__format__'''
class  foo:
    def __init__(self,year,moth,day):
        self.year=year
        self.moth=moth
        self.day=day
    def __format__(self, format_spec):
        dict=\
        {
            'ymd':'{}{}{}'.format(self.year,self.moth,self.day),
            'y:m:d':'{}:{}:{}'.format(self.year,self.moth,self.day),

        }

        return dict[format_spec]
f=foo(2019,4,8)
print(format(f,'ymd'))

print(format(f,'y:m:d'))

#201948
# 2019:4:8

__slots__属性的方法介绍

  • 节约空间

案例1:如果类中定义了__slots__方法,则实例不能使用使用__dict__

class FOO:
    __slots__=['foo']
    def __init__(self):
        pass

print(FOO.__dict__)
f=FOO()
print(f.__dict__)
#如果类中定义了__slots__方法,则实例不能使用使用__dict__
#报错信息如下:AttributeError: 'FOO' object has no attribute '__dict__'

案例2: 实例只能使用__slots__中的属性

'''__slots__'''
#'https://www.cnblogs.com/rainfd/p/slots.html'
class FOO:
    __slots__=['foo']
    def __init__(self):
        pass

print(FOO.__dict__)
f=FOO()
f.foo=123
print(f.foo) #实例只能使用__slots__中的属性

案例3:只读

'''__slots__'''
#'https://www.cnblogs.com/rainfd/p/slots.html'
class FOO:
    __slots__=('foo')
    foo1=123
    def __init__(self):
        pass

print(FOO.__dict__)
f=FOO()
f.foo1=256
print(f.foo1)#定义了__slots__后,类属性是只读的
#AttributeError: 'FOO' object attribute 'foo1' is read-only

案例4:省内存,访问更快

  • 没有使用__slots__的解释器内存
from memory_profiler import profile
class FOO:

    def __init__(self,x):
        self.x=x


@profile
def main():
    f = [FOO(523825) for i in range(100)]


if __name__ == '__main__':
    main()
  • 结果
Line #    Mem usage    Increment   Line Contents
================================================
     8     16.3 MiB     16.3 MiB   @profile
     9                             def main():
    10     33.8 MiB      0.6 MiB       f = [FOO(523825) for i in range(100000)]

使用__slots__后

from memory_profiler import profile
class FOO:
    __slots__ = 'x'
    def __init__(self,x):
        self.x=x


@profile
def main():
    f = [FOO(523825) for i in range(100000)]


if __name__ == '__main__':
    main()

结果

Line #    Mem usage    Increment   Line Contents
================================================
     8     15.5 MiB     15.5 MiB   @profile
     9                             def main():
    10     21.1 MiB      0.4 MiB       f = [FOO(523825) for i in range(100000)]

属性__doc__

  • 这个属性是不能被继承的
  • 这个属性展示对象的文档信息
class FOO:
    '''我是foo的文档信息'''
    def __init__(self):
        pass
print(FOO.__doc__)
# 我是foo的文档信息
FOO.__dict__.pop('__doc__')
#AttributeError: 'mappingproxy' object has no attribute 'pop'

属性__module__和__class__

  • module:查看对象是来源那个模块
  • class:查看对象来源于哪个类

析构方法:del

  • 当对象在内存中被释放时,自动触发执行.此方法一般无需定义
  • 这个方法在实例删除的时候触发,即开始回收内存的时候

案例1;在对象释放时调用

class  FOO:
    def __init__(self):
        pass
    def __del__(self):
        print('开始资源回收了')
f=FOO()
print('我是程序中的打印')

# 我是程序中的打印
# 开始资源回收了

案例2:当我们开始删除实例的时候,会先触发类中的del方法,效果如下:

class  FOO:
    def __init__(self,name):
        self.name=name
    def __del__(self):
        print('开始资源回收了')
f=FOO('小明')
del f  
print('我是程序中的打印')

# 开始执行实例的删除操作后,真相来了
# 执行结果------>
# 开始资源回收了
# 我是程序中的打印

__call__方法

  • 对象后面可以加括号运行,触发执行__call__方法中的内容

案例1:一般的实例后面不能直接加括号运行,如下,报错信息:TypeError: ‘FOO’ object is not callable

class  FOO:
    def __init__(self,name):
        self.name=name
    def __del__(self):
        print('开始资源回收了')
f=FOO('小明')
f()

# 执行结果------>
# TypeError: 'FOO' object is not callable
# 开始资源回收了



案例2:但是如果我在类中实现了__call__方法的时候,则当实例(),或者类()(),就会触发这个方法中的内容

class  FOO:
    def __init__(self,name):
        self.name=name
    def __call__(self, *args, **kwargs):
        print('我时__call__方法')
    def __del__(self):
        print('开始资源回收了')
f=FOO('小明')
f()
FOO('小花')()

# 执行结果如下:
# 我时__call__方法
# 我时__call__方法
# 开始资源回收了
# 开始资源回收了

使用__next__和__iter__实现迭代器协议

  • 迭代器协议:对象提供一个next方法,执行这个方法,返回迭代中的下一项,直到触发StopIteration来终止迭代
  • 可迭代对象:实现了迭代器协议的对象(如何实现,在对象的内部定义一个__next__方法)
  • python内部实现这种迭代器协议的对象有,for循环,sum.min.max
    -当内部的对象有__iter__方法时,可以变为可迭代对象,比如list中,没有__next__,但是内部存在__iter__,就可以变为可迭代对象

案例1:对象中没有实现__iter__,也不是可迭代对象(实现__next__方法的对象),当for这种遍历的时候,就会报错

class FOO:
    def __init__(self,default=1):
        self.default=default

f=FOO(2)
for i in f:
    print()
    
# 如果对象中没有实现__iter__方法,也不会是可迭代对象
# TypeError: 'FOO' object is not iterable

案例2:对象实现了__iter__方法后

class FOO:
    def __init__(self,default=1):
        self.default=default
    def __iter__(self):
        return self
    def __next__(self):
        if self.default !=100:
            self.default+=1
            return self.default
        else :
            raise StopIteration


f=FOO(2)

for i in f:
    print(i)
# 执行结果1-100
使用迭代器协议实现斐波那契数列
  • 知识点1:next()其实就是调用__next__()方法
class FOO:
    def __init__(self):
        self.a=0
        self.b=1
    def __iter__(self):
        return self
    def __next__(self):
        if self.a==10:
            raise StopIteration
        else:
            self.a,self.b=self.b,self.a+self.b
            return self.a



f=FOO()
print('这是__next__的执行效果')
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print('这是next()的执行效果')
print (next(f))#next()其实就是调用__next__()方法
print (next(f))
print (next(f))
print (next(f))
print (next(f))
print (next(f))

# 这是__next__的执行效果
# 1
# 1
# 2
# 3
# 5
# 这是next()的执行效果
# 8
# 13
# 21
# 34
# 55
# 89

描述符

  • 实现了__delete()__,set(),__get()__其中一个方法的就是描述符
  • 描述符只能用来代理一个类的属性,不能放在构造函数中
  • 描述符只属于类不属于实例
  • 数据描述符:至少实现了__set()__,__get()__两种方法的
  • 非数据描述符:没有实现__set()__,但是实现了其他的方法的
  • 描述符时用来代理另外一个类中的属性,所以如果在同一个类中使用,并不会执行
  • 描述符在另外一个类中的类属性中进行使用
class FOO:
    def __init__(self):
        self.a=0
        self.b=1
    def __iter__(self):
        return self
    def __next__(self):
        if self.a==10:
            raise StopIteration
        else:
            self.a,self.b=self.b,self.a+self.b
            return self.a
    def __get__(self, instance, owner):
        print('我是__get__方法')
    def __set__(self, instance, value):
        print('我是__set__方法')
    def __delete__(self, instance):
        print('我是__delete__方法')


class dect:#描述符是在代理另外的类
    x=FOO()  #描述符只能用于类属性


d=dect()
d.x='123' #赋值的时候会触发__set__方法-------->我是__set__方法
print(d.__dict__)


描述符的种类及优先级

类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()

判断属性 x 为一个描述符,此时,它就会做一些变动了,将 TestDesc.x 转化为 TestDesc.__dict__['x'].__get__(None, TestDesc) 来访问
描述符的应用
  • 实现输入的类型的矫正
'''实现用户输入的类型的判断,比如用户输入的名字是str,年龄是int'''

class Person:
    def __init__(self, keyName,typeNew):
        self.keyName = keyName  #类型的名字
        self.type = typeNew    #新的类型限制
    def __get__(self, instance, owner):
       return instance.__dict__[self.keyName]

    def __set__(self, instance, value):
      if isinstance(value,self.type):
          print(instance)
          instance.__dict__[self.keyName]=value
      else:
          raise  AttributeError

    def __delete__(self, instance):
        instance.__dict__.pop[self.keyName]
class female:
    name = Person('name',str)  # name和age 被Person()描述了
    age = Person('age',int)
    def __init__(self,name,age):
        self.name=name
        self.age=age


f=female('小花',18)
print(f.name)
print(f.__dict__)
# 终于搞出来了,一开始错误的原因就是在__Set__中使用的是if isinstace(instance,self.type)
# 这样是不对的,因为instance的class是female,不可能是str输入类型的实例的,应该判断value值
# 是不是很蠢

# 执行结果:
#小花
# {'name': '小花', 'age': 18}

上下文管理协议

  • enter
  • exit:如果代码没有错误,会在最后执行,如果发生异常时会直接运行__exit__,从而下面的代码不会执行,在需要管理一些资源,比如文件,网络链接和锁的编程环境中,可以在__exit__中定制自动释放的机制,无需再去关心这个问题,这将大有用处

案例:上下文管理协议实现open文件操作

异常的构成

Traceback (most recent call last):
我是__set__方法
我是__get__方法
  File "C:/tools/pycharm/subject/0409.py", line 31, in <module>
    d.x
AttributeError: 'dect' object has no attribute 'x'

  • 异常的第一个是回溯信息
  • 然后是报错点
  • 最后是报错的信息
异常的组成
ython 内建异常
BaseExceptiona 所有异常的基类
SystemExitb python 解释器请求退出
KeyboardInterruptc 用户中断执行(通常是输入^C)
Exceptiond 常规错误的基类
StopIteratione 迭代器没有更多的值
GeneratorExita 生成器(generator)发生异常来通知退出
SystemExith Python 解释器请求退出
StandardErrorg 所有的内建标准异常的基类
ArithmeticErrord 所有数值计算错误的基类
FloatingPointErrord 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionErrord 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentErrord 操作系统错误的基类
IOError 输入/输出操作失败
OSErrord 操作系统错误
WindowsErrorh Windows 系统调用失败
ImportError 导入模块/对象失败
KeyboardInterruptf 用户中断执行(通常是输入^C)
LookupErrord 无效数据查询的基类
IndexError 序列中没有没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalErrorh 访问未初始化的本地变量
ReferenceErrore 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedErrord 尚未实现的方法
SyntaxError Python 语法错误
IndentationErrorg 缩进错误
TabErrorg Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeErrorh Unicode 相关的错误
UnicodeDecodeErrori Unicode 解码时的错误
UnicodeEncodeErrori Unicode 编码时错误
UnicodeTranslateErrorf Unicode 转换时错误
Warningj 警告的基类
DeprecationWarningj 关于被弃用的特征的警告
FutureWarningi 关于构造将来语义会有改变的警告
OverflowWarningk 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarningi 关于特性将会被废弃的警告
RuntimeWarningj 可疑的运行时行为(runtime behavior)的警告
SyntaxWarningj 可疑的语法的警告
UserWarningj 用户代码生成的警告

装饰器

  • 装饰器其实就是一个函数,可以增加被装饰的对象的功能
  • 实现机制

类的装饰器

  • 被装饰的对象是类的装饰器,就是类装饰器
'''装饰器和类装饰器'''
'''此代码实现对类型的指定选择'''

def des(propertys):#最外面一层是接受参数的函数
 def descripter(func):
     # print('好好学习')#这里可以写装饰的逻辑
     def wapper(*args):
         print(func.__dict__)
         return func
     return wapper

@des(name=str)
class female:
    def __init__(self,name,age):
        self.name=name
        self.age=age

f=female('小明',18)

  • 装饰器是提供对函数调用和函数对象管理,类装饰器提供对类对象和类本身的管理
  • 函数装饰器用来增强一个函数的共嗯,而不是直接提供一个接口
  • 类装饰器可以用来拦截或扩展实例方法
函数闭包
  • 闭包:在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
  • 函数闭包是不能修改外面的变量的值的
def foo():
    m=0
    def foo1():
        m=1
        print(m)
    foo1()
    print(m)

if __name__=='__main__':
   foo()
# 1
# 0
# 所以内嵌的函数不能改变外面的变量的值,作用域不同
# 如果要改变外面的值,需要将外面变量设置为容器或者是nonlocal

案例2:注意下面这个函数

flist = []  
for i in range(3):  
    def foo(x):
        print (x + i )
    flist.append(foo)  
for f in flist:  
    f(2)
    # 可能有些人认为这段代码的执行结果应该是2, 3, 4.
    # 但是实际的结果是4, 4, 4。这是因为当把函数加入flist列表里时,python还没有给i赋值,只有当执行时,再去找i的值是什么,这时在第一个for循环结束以后,i的值是2,所以以上代码的执行结果是4, 4, 4.

案例:描述符与装饰器的综合使用

'''装饰器和类装饰器'''
'''此代码实现对类型的指定选择'''
'''修改属性'''

class Wapper: #描述符用来对类型进行判断
    def __init__(self,typeName,typed):
            self.typeName=typeName
            self.typed=typed
    def __get__(self, instance, owner):
        return instance.__dict__[self.typeName]
    def __set__(self, instance, value):
        if isinstance(value, self.typed):
            instance.__dict__[self.typeName] = value
        else:
            raise AttributeError  #输入的类型不符合条件是抛出异常
            #print('%s输入类型不符合要求'%self.typeName)

    def __delete__(self, instance):
        instance.__dict__.pop(self.typeName)


def des(**kwargs):#最外面一层是接受参数的函数
  def descripter(func):
         for key,val in kwargs.items():
            print('执行前')
            print(key)
            setattr(func,key,Wapper(key,val))
             # 划重点
             #等价于func.key=Wapper(key,val)
             #将描述符对象(用来对类型进行判断),给func.key,这个需要先走一遍描述符
         return func
  return descripter

@des(name=str,age=int)
class female:
    def __init__(self,name,age,weight,gender):
        self.name=name
        self.age=age
        self.weight=weight
        self.gender=gender


f=female('小明',18,58,'男')

自定义property

  • 描述符可以实现pytho类属性中的底层魔法方法 @staticmethod,@staticmethod ,@property
class NewProperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print(self)
        print(instance)
        print(owner)
        print(self.func)
        res=self.func(instance)#area(self)
        setattr(instance, self.func.__name__, res)
        return res


class Room:
    def __init__(self,length,width):
        self.length=length
        self.width=width
    @NewProperty
#area=property(area)
# func=property(func)
#所以描述符中需要传入参数func
    def area(self):#计算面积
        return self.width*self.length


r=Room(12,14)
print(r.area)  #等价于Room.area(r)

property流程分析

self探究

1. self 是什么

class Test:
    def prt(th):
        print(th)
        #slef--> 类的实例
        #self写成其他形式比如,上面的th也是一样的结果
        #self一定要写,否则会报错
        print(th.__class__)
t=Test()
t.prt()
2. self 必须要写
'''self一定要写'''

class Test:
    def prt():
        print(self)
        print(self.__class__)
t=Test()
t.prt()
# self这个参数一定要写,否则会报错
# Traceback (most recent call last):
#   File "C:/tools/pycharm/subject/atest.py", line 11, in <module>
#     t.prt()
# TypeError: prt() takes 0 positional arguments but 1 was given
在继承中self由其实例的类决定,不需要特别在乎继承的类,继承类得到的方法
class Parent:
    def pprt(self):
        print(self)

class Child(Parent):
    def cprt(self):
        print(self)
c = Child()
c.cprt()
c.pprt() ##子类继承父类得到pprt方法,但是self还是子类自己的类实例
p = Parent()
#<__main__.Child object at 0x000001D6C6C97A58>
# <__main__.Child object at 0x000001D6C6C97A58>
# <__main__.Parent object at 0x000001D6C6C97AC8>
p.pprt()

元类

socket编程

什么是socket编程
  • 当应用层和传输层进行数据通信时,会发送IP地址,接口,传输层的协议(TCP/UDP),socket 相当于一个插座,可以处理多个协议通过同一个端口的数据并发问题或者多个应用程序之间的通信
套接字
  • 套接字起源于BSBunix
  • 刚开始被设计用来在一个主机上多个应用程序之间的通信,也被称为继承间通讯或IPC
  • 套接字的种类:基于文件型和基于网络型
  • 进行之间是不能进行直接通信的
基于文件的套接字
  • 可以实现一台系统中程序之间的通信
  • AF_UNIX
基于网络的套接字

*可以实现多个机器之间的通信

  • AF_INET
套接字工作流程

案例:服务器与客户端之间的通信
服务器端

#手机开机-最多5个链接,就是半连接池的作用
# 防止SYN洪水攻击的方法,就是增大半连接池的数量或者缩小请求返回的时间
conn,addr=phone.accept()
# 相当于拿到TCP连接 ack=y+1
#等电话得到电话链接conn和对方手机号addr
message=conn.recv(1024)
print('客户端发来的消息是:',message)
#接受传输,收到1024个字节的信息,收发消息是二进制的
#开始进行通信
conn.send(message.upper())
#发消息-返回消息的大写
conn.close()
#触发四次挥手的过程
#挂电话
phone.close()
#手机关机

##客户端发来的消息是: b'abc'

客户端

import socket
cc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买手机
cc.connect(('127.0.0.1',8000))

cc.send('abc'.encode('utf-8'))
print('客户端发送消息')
data=cc.recv(1024)
print('服务器端返回的消息',data)
#基于网络,要传输二进制,所以要encode
cc.close()


#客户端发送消息
# 服务器端返回的消息 b'ABC'

socket底层工作原理
  • sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
    参数一:地址簇

socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6

socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

参数二:类型

socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP

socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET 可靠的连续数据包服务

参数三:协议
 (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

  • recv(1024)其中1024代表首发的字节数,拉取缓存区中的数据
    服务器或者客户端按照队列的方式拉数据

  • phone.listen(5)
    #手机开机-最多5个链接,超过的链接会放在连接池中

  • sk.bind(address)

s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

  • sk.listen(backlog)

开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

  backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
  这个值不能无限大,因为要在内核中维护连接队列
socket编程实现循环发
  • 需要在各自的接受与发送代码中添加循环
  • client端
import socket
cc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买手机
cc.connect(('127.0.0.1',8020))
for  i in range(1,10):
    cc.send('abc'.encode('utf-8'))
    print('客户端发送消息')
    data=cc.recv(1024)
    print('服务器端返回的消息',data)
#基于网络,要传输二进制,所以要encode
cc.close()


#客户端发送消息
# 服务器端返回的消息 b'ABC'
  • server端
import  socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买手机-基于网络通信-基于TCP协议
phone.bind(('127.0.0.1',8020))
#手机绑定电话卡
phone.listen(5)
#手机开机-最多5个链接
conn, addr = phone.accept()
# 等电话得到电话链接conn和对方手机号addr
for  i in range(1,10):
    message=conn.recv(1024)
    print('客户端发来的消息是:',message)
    #接受传输,收到1024个字节的信息,收发消息是二进制的
    conn.send(message.upper())
#发消息-返回消息的大写
conn.close()
#挂电话
phone.close()
#手机关机

##客户端发来的消息是: b'abc'

案例:如何实现一个服务端与多个客户端交互,玩了一下午的这个socket编程,真的很好玩

  • server
import  socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买手机-基于网络通信-基于TCP协议

phone.bind(('127.0.0.1',8030))
#手机绑定电话卡
phone.listen(5)
while True:
    #手机开机-最多5个链接,超过的链接会放在连接池中
    conn, addr = phone.accept()
    # 等电话得到电话链接conn和对方手机号addr
    '''
    socket对象<socket.socket fd=536, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8020), raddr=('127.0.0.1', 59823)>
    客户端地址('127.0.0.1', 59823)
    '''
    for i in range(1, 10):
        message=conn.recv(1024)
        if message is not None:
            if message is int or str:
              #接受传输,收到1024个字节的信息,收发消息是二进制的
              conn.send(message.upper())
        else:
            break
        #发消息-返回消息的大写
    conn.close()
    #挂电话
phone.close()
    #客户端发来的消息是: b'abc'
  • client:我在这个客户端加了聊天停止命令
import socket
cc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买手机
cc.connect(('127.0.0.1',8030))
flag=True
while flag:
    mes=input('输入')
    if mes is not 'q':
        cc.send(mes.encode('utf-8'))
        data=cc.recv(1024)
        print('服务器端返回的消息',data)
    else :
       cc.close()
       break
#基于网络,要传输二进制,所以要encode


#客户端发送消息
# 服务器端返回的消息 b'ABC'

  • client1
import socket
cc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买手机
cc.connect(('127.0.0.1',8030))
for  i in range(1,10):
    cc.send('xyz'.encode('utf-8'))
    print('客户端发送消息')
    data=cc.recv(1024)
    print('服务器端返回的消息',data)
#基于网络,要传输二进制,所以要encode
cc.close()


#客户端发送消息
# 服务器端返回的消息 b'ABC'

-异常问题解决,总是占用一个端口和地址

Traceback (most recent call last):
  File "C:/tools/pycharm/subject/socket1/server.py", line 4, in <module>
    phone.bind(('127.0.0.1',8000))
OSError: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted
  • 重点第二句
  • 第二就话就是解决的方案
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买手机-基于网络通信-基于TCP协议
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8030))

基于UDP的套接字

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值