您的位置:新葡亰496net > 奥门新萄京娱乐场 > 新葡亰496net:继承与多态,面向对象之

新葡亰496net:继承与多态,面向对象之

发布时间:2019-06-29 23:50编辑:奥门新萄京娱乐场浏览(186)

    周末多码文,昨天晚上一篇,今天再来一篇:

     

     

     

    在线编程:

    本文适应人群:C# or Python3 基础巩固

    代码裤子:

    代码裤子:

    在线预览:http://github.lesschina.com/python/base/oop/3.异常相关.html

    代码裤子:

    在线编程:https://mybinder.org/v2/gh/lotapp/BaseCode/master
    在线预览:http://github.lesschina.com/python/base/oop/2.继承与多态.html

    在线编程:

    代码裤子:

    在线编程:

    2.继承 ¶

    在线预览:http://github.lesschina.com/python/base/module/模块专题.html

     

    在线预览:http://github.lesschina.com/python/base/ext/基础衍生.html

    2.1.单继承 ¶

    在OOP中,当我们定义一个Class的时候,可以从某个现有的Class继承

    新的Class称为子类,而被继承的class称为 基类 或者 父类

    Python的继承格式 ==> xxx(base_class)

    小明兴高采烈的听着老师开新课,不一会就看见了一个演示Demo:

    In [1]:

    class Animal(object):
        def eat(self):
            print("动物会吃")
    
    class Cat(Animal):
        # 注意一下Python的继承格式
        pass
    
    class Dog(Animal):
        pass
    
    def main():
        cat = Cat()
        dog = Dog()
        cat.eat()
        dog.eat()
    
    if __name__ == "__main__":
        main()
    

     

    动物会吃
    动物会吃
    

     

    当听到老师说:“ 私有的属性方法 不会被子类继承 ”的时候,小明心里一颤,联想到之前讲的 类属性实例属性实例方法类方法静态方法,于是赶紧写个Demo验证一下:

    In [2]:

    class Animal(object):
        # 类属性
        name = '动物'
    
        def __init__(self):
            # 实例属性
            self.age = 1
    
        def __bug(self):
            """实例私有方法"""
            print("我是动物类身上的私有方法:bug")
    
        def eat(self):
            """实例方法"""
            print("我是实例方法,动物会吃哦~")
    
        @classmethod
        def call(cls):
            """类方法"""
            print("我是类方法,动物会叫哦")
    
        @staticmethod
        def play():
            """静态方法"""
            print("我是静态方法,动物会玩耍哦")
    
    
    class Dog(Animal):
        pass
    
    
    def main():
        dog = Dog()
        # 实例属性
        print(dog.age)
        # 实例方法
        dog.eat()
    
        # 类属性
        print(dog.name)
        # 类方法
        dog.call()
        Dog.call()
        Animal.call()
    
        # 静态方法
        dog.play()
        Dog.play()
        Animal.play()
    
    
    if __name__ == '__main__':
        main()
    

     

    1
    我是实例方法,动物会吃哦~
    动物
    我是类方法,动物会叫哦
    我是类方法,动物会叫哦
    我是类方法,动物会叫哦
    我是静态方法,动物会玩耍哦
    我是静态方法,动物会玩耍哦
    我是静态方法,动物会玩耍哦
    

     

    来张图就懂了,不是 私有的 都能访问:

    新葡亰496net 1

    这时候,小明老高兴了,单回头一想 ==> 不科学啊,dog应该有其对应的方法吧,C#有 虚方法重写,Python怎么搞?在 子类里面又 怎么调用父类方法呢?

    对于小明的提示老师很高兴,于是点名小潘来写一个子类调用父类的demo(老师昨天从窗户里看见小潘有预习):

    In [3]:

    # 调用父类的方法
    class Father(object):
        def eat(self):
            print("文雅的吃饭")
    
    
    class Son(Father):
        def eat(self):
            # 调用父类方法第1种(super().方法)
            super().eat()
    
    
    class GrandSon(Son):
        def eat(self):
            # 调用父类方法第2种(记得传self)
            Son.eat(self)
    
    
    def main():
        xiaoming = Son()
        xiaoming.eat()
    
        xiaoli = GrandSon()
        xiaoli.eat()
    
    
    if __name__ == '__main__':
        main()
    

     

    文雅的吃饭
    文雅的吃饭
    

     

    一般我们使用 super().方法来调用父类方法

    第二种方法 类名.方法(self)千万别忘记传self哦

    对了,C#是用base关键词,别搞混了

    小明这时候可不高兴了,风头怎么能被小潘全部抢走呢,赶紧问问旁边同样预习的伟哥

    不一会儿淡定的发了份重写父类方法的demo给老师:

    In [4]:

    # 重写父类方法==>子类和父类有同名方法
    class Father(object):
        def __init__(self, name):
            self.name = name
    
        def eat(self):
            print("%s喜欢文雅的吃饭" % self.name)
    
    
    class Son(Father):
        def __init__(self, name):
            super().__init__(name)
    
        def eat(self):
            print("%s喜欢大口吃饭大口喝酒" % self.name)
    
    
    def main():
        xiaoming = Father("小明")
        xiaoming.eat()
    
        xiaopan = Son("小潘")
        xiaopan.eat()
    
    
    if __name__ == "__main__":
        main()
    

     

    小明喜欢文雅的吃饭
    小潘喜欢大口吃饭大口喝酒
    

     

    老师半喜半忧的说道:“小明同学啊,你也老大不小了,怎么跟孩子一样啊?案例不错,但是怎么能人身攻击人家小潘了?”

    当子类和父类都存在相同的 eat()方法时,我们说,子类的 eat()覆盖了父类的 eat()

    在代码运行的时候,总是会调用子类的 eat() 这样,我们就获得了继承的另一个好处: 多态

    老师休假度蜜月去了,这学期也到了尾声,小明同学觉得自己学的挺好的,在四处逛游的过程中看见了小潘的电脑,然后惊到了!

    1.异常¶

    马上快期末考试了,老师蜜月也回来了,于是有了一场跨季度的复习讲课了:

    2.2.多继承 ¶

    在讲 多态之前,我们先引入一下Python的 多继承 对,你没有听错

    Java、C#都是单继承,多实现。Python和C 一样,可以多继承,先不要吐槽, 规范使用其实很方便的

    来个案例看看:

    In [5]:

    # 多继承引入
    class Father(object):
        def eat(self):
            print("文雅的吃饭")
    
    
    class Mom(object):
        def run(self):
            print("小碎步")
    
    
    class Son(Father, Mom):
        pass
    
    
    def main():
        son = Son()
        son.eat()
        son.run()
    
    
    if __name__ == '__main__':
        main()
    

     

    文雅的吃饭
    小碎步
    

     

    继承可以把父类的所有功能都直接拿过来,这样就不必重0开始写代码,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写

    注意一个情况,如果父类里面有同名方法咋办了?到底调哪个呢?

    使用 子类名.__mro__可以看在调方法的时候搜索顺序

    一般同名方法都是 先看自己有没有,然后看继承顺序,比如这边 先看Mom再看Father

    In [6]:

    # 如果父类里面有同名方法怎么知道调哪个?
    class Father(object):
        def eat(self):
            print("文雅的吃饭")
    
    
    class Mom(object):
        def eat(self):
            print("开心的吃饭")
    
    
    class Son(Mom, Father):
        pass
    
    
    def main():
        son = Son()
        son.eat()
        print(Son.__mro__)  # 一般同名方法都是先看自己有没有,然后看继承顺序,比如这边先看Mom再看Father
    
    
    if __name__ == '__main__':
        main()
    

     

    开心的吃饭
    (<class '__main__.Son'>, <class '__main__.Mom'>, <class '__main__.Father'>, <class 'object'>)
    

     

    Python的多继承最好是当C#或者Java里面的接口使用,这样结构不会混乱( 特殊情况除外)

    来个例子:

    class Animal(object):
        pass
    
    class Flyable(object):
        """飞的方法"""
        pass
    
    class Runable(object):
        """跑的方法"""
        pass
    
    class Dog(Animal, Runable):
        pass
    
    class Cat(Animal, Runable):
        pass
    
    class Bird(Animal, Flyable):
        pass
    
    class Dack(Animal, Runable, Flyable):
        """鸭子会飞也会跑"""
        pass
    

    和C#一样,Python的 父类构造函数不会被继承

    其实从资源角度也不应该被继承,如果有1w个子类,那每个子类里面都有一个父类方法,想想这是多么浪费的一件事情?


    In [1]:

    1.1 try...except¶

    又到了开新课的时候了,小明同学这次提前预习了知识,乘着老师还没来就在黑板上写下了这段Code:

    In [1]:

    def main():
        try:
            1 / 0  # ZeroDivisionError: division by zero
        except ZeroDivisionError as ex:
            print(ex)
    
    
    if __name__ == '__main__':
        main()
    

     

    division by zero
    

     

    1.Python基础语法扩展¶

    2.3.C#继承 ¶

    下课后,小明认真思考总结,然后对照Python写下了C#版的继承:

    定义一个人类

    public class Person
    {
        public string Name { get; set; }
        public ushort Age { get; set; }
    
        public Person(string name, ushort age)
        {
            this.Name = name;
            this.Age = age;
        }
        public void Hi()//People
        {
            Console.WriteLine("Name: "   this.Name   " Age: "   this.Age);
        }
        public virtual void Show()//People
        {
            Console.WriteLine("Name: "   this.Name   " Age: "   this.Age);
        }
    }
    

    定义一个学生类

    public class Student : Person
    {
        #region 属性
        /// <summary>
        /// 学校
        /// </summary>
        public string School { get; set; }
        /// <summary>
        /// 班级
        /// </summary>
        public string StrClass { get; set; }
        /// <summary>
        /// 学号
        /// </summary>
        public string StrNum { get; set; }
        #endregion
    
        #region 构造函数
        /// <summary>
        /// 调用父类构造函数
        /// </summary>
        /// <param name="name"></param>
        /// <param name="age"></param>
        public Student(string name, ushort age) : base(name, age)
        {
    
        }
        public Student(string name, ushort age, string school, string strClass, string strNum) : this(name, age)
        {
            this.School = school;
            this.StrClass = strClass;
            this.StrNum = strNum;
        } 
        #endregion
    
        /// <summary>
        /// new-隐藏
        /// </summary>
        public new void Hi()//Student
        {
            Console.WriteLine("Name: "   this.Name   " Age: "   this.Age   " School: "   this.School   " strClass: "   this.StrClass   " strNum: "   this.StrNum);
        }
        /// <summary>
        /// override-覆盖
        /// </summary>
        public override void Show()//Student
        {
            Console.WriteLine("Name: "   this.Name   " Age: "   this.Age   " School: "   this.School   " strClass: "   this.StrClass   " strNum: "   this.StrNum);
        }
    }
    

    调用一下:

    Person p = new Student("app", 10, "北京大学", "001", "01001");
    p.Hi(); p.Show();
    
    Console.WriteLine();
    
    Student s = p as Student;
    s.Hi(); s.Show();
    

    结果:

    Name: app Age: 10
    Name: app Age: 10 School: 北京大学 strClass: 001 strNum: 01001
    Name: app Age: 10 School: 北京大学 strClass: 001 strNum: 01001
    Name: app Age: 10 School: 北京大学 strClass: 001 strNum: 01001
    

    # 导入matplotlib的pyplot模块
    import matplotlib.pyplot as plt
    
    x_list=list(range(1,11))
    y_list=[y*2 1 for y in x_list]
    
    print(x_list)
    print(y_list)
    

    1.2 try...except...else...finally¶

    小潘同学刚进来就看见了,自语道:“try...except捕获异常谁不会?就会这么屁点东西还好意思秀,切~ 我给你把 格式补全”

    于是乘着小明上厕所的时候,擦掉小明的Code,自己写了一段高大上的Code:

    In [2]:

    # 异常捕获全格式
    def test(input_str):
        try:
            eval(input_str)
        except ZeroDivisionError as ex:
            print("except:", ex)
        else:
            print("else:没有异常就奖励100块~")
        finally:
            print("finally:小明是傻子~")
    
    
    def main():
        test("1/0")
        print("-" * 10)
        test("print('小明啊小明你调坑里了~')")
    
    
    if __name__ == '__main__':
        main()
    

     

    except: division by zero
    finally:小明是傻子~
    ----------
    小明啊小明你调坑里了~
    else:没有异常就奖励100块~
    finally:小明是傻子~
    

     

    这时候小明和老师一起进来了,同学们隐约间都听到小明的自夸声:“老师,我可好了,提前预习并且还写了个demo在黑板上呢~”

    老师一进门看着黑板就笑了,同学们也笑成一片。小明心想,咦~难道我写错了?定眼一看黑板,气呼呼的回座位了

    else可以不写,不过我们 基本上还是会写的,毕竟可以知道是真的没有错误,而不是屏蔽了错误

    1.1.if 判断条件相关¶

    None、""、0、[]、{} ==> 假

    1、" "、[None,""]、{"":None} ==> 真

    小明可高兴了,前几天被打击的面目全非,这几天老师回来了,又可以大发神威了,于是抢先提交demo:

    In [1]:

    # None
    if None:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [2]:

    # 0为False
    if 0:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [3]:

    # 空字符串
    if "":
        print(True)
    else:
        print(False)
    

     

    False
    

    In [4]:

    # 空列表为False
    if []:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [5]:

    # 空字典为False
    if {}:
        print(True)
    else:
        print(False)
    

     

    False
    

    In [6]:

    # 1为True
    if 1:
        print(True)
    else:
        print(False)
    

     

    True
    

    In [7]:

    # 含空格
    if " ":
        print(True)
    else:
        print(False)
    

     

    True
    

    In [8]:

    if [None,""]:
        print(True)
    else:
        print(False)
    

     

    True
    

    In [9]:

    if {"":None}:
        print(True)
    else:
        print(False)
    

     

    True
    

     

    老师微带笑容的看了小明一眼,然后接着讲if的扩展

    2.4C#接口的多实现 ¶

    定义两个接口:

    public interface IRun
    {
        //什么都不用加
        void Run();
    }
    
    public interface IEat
    {
        void Eat();
    }
    

    定义一个Dog类来实现两个接口,这样dog就有了run和eat的方法了

    var dog = new Dog();
    dog.Eat();
    dog.Run();
    

    结果:

    狗狗吃
    狗狗跑
    

     

    1.3 多个异常处理¶

    老师很欣慰,觉得这个班真有意思,大家学习空前热情,为了照顾小明,老师反问道:“有谁知道 多个异常怎么处理?”

    小明飞快的举手并把黑板上内容擦完,写下了如下代码:

    In [3]:

    # 多个异常捕获
    def main():
        try:
            print(xiaopan)  # NameError: name 'xiaopan' is not defined
            1 / 0  # ZeroDivisionError: division by zero
        except NameError as ex:
            print(ex)
        except ZeroDivisionError as ex:
            print(ex)
    
    if __name__ == '__main__':
        main()
    

     

    name 'xiaopan' is not defined
    

     

    老师问了小明一声,有几个输出?

    小明骄傲的说道:“两个,我写了两个异常处理,当然都执行了”

    同学们又笑了,小潘调侃的说了句:“一看就知道去年C#没好好学,这不都一样嘛,遇到异常下面代码还执行吗?用脑子好好想想”

    当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至except语句块,执行完except后,如果有finally语句块,则执行finally语句块

    小明又尴尬了。。。

    1.2.三元表达符¶

    eg:max = a if a > b else b

    In [10]:

    a, b = 1, 2
    
    max = a if a > b else b
    
    print(max)
    

     

    2
    

    In [11]:

    a, b, c = 1, 3, 2
    
    max = a if a > b else b
    max = max if max > c else c
    
    print(max)
    

     

    3
    

    In [12]:

    # 上面的那个还有一种简写(不推荐)
    a, b, c = 1, 3, 2
    
    max = (a if a > b else b) if (a if a > b else b) > c else c
    
    print(max)
    

     

    3
    

     

    3 多态 ¶

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    [3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
    

    1.4 多异常简写¶

    老师再次帮小明圆了个场:“已经很不简单了,就是最后小得意的时候口误了,那小明同学你知道Python里面多异常有个便捷写法吗?”

    小明赶紧拿起粉笔刷刷刷的写完,然后说道:“of course

    In [4]:

    # 多个异常捕获的简写(注意哦,是元组哦)
    def main():
        try:
            print(xiaopan)  # NameError: name 'xiaopan' is not defined
            1 / 0  # ZeroDivisionError: division by zero
        except (NameError, ZeroDivisionError) as ex:
            print(ex)
    
    
    if __name__ == '__main__':
        main()
    

     

    name 'xiaopan' is not defined
    

     

    老师赶紧夸了夸小明,心想,哎呦喂终于把这难缠的家伙弄回座位了。

    小明走前还不忘说一句:“简写的时候注意格式哦,是 元组 不是逗号分隔”

    老师这堂课很轻松,大家都预习了而且内容也比较简单。

    接着以提问的方式问道:“小潘同学,你知道异常的基类是什么吗?如果要捕获所有异常该怎么做呢?”

    小潘站起来说道:“是BaseException

    老师扩充道:“所有的错误类型都继承自BaseException,所以在使用except时需要注意的是,它不但捕获该类型的错误,还把其子类也一起捕获了”

    所以一般在捕获异常的时候 把子类异常放在前面,父类放在后面

    看如下代码:

    In [5]:

    def main():
        try:
            1 / 0  # ZeroDivisionError: division by zero
        except BaseException as ex:
            print("base:", ex)
        except ZeroDivisionError as ex:
            print(ex)
    
    
    if __name__ == '__main__':
        main()
    

     

    base: division by zero
    

     

    如果把父类放第一个,那么ZeroDivisionError永远也不会被执行了,其实你如果装了 代码规范提示插件会提示你的

    可以参考我之前写的 vscode设置python3调试环境的扩充部分

    来个通用异常捕获的简写(官方不推荐使用简写):

    In [6]:

    # 直接except就行了
    def main():
        try:
            1 / 0
            dnt  = 1
        except:
            print("屏蔽错误")
    
    
    if __name__ == '__main__':
        main()
    

     

    屏蔽错误
    

     

    老师继续讲到,我们来看一个场景,现在很多在线编辑器,你在他们那些编辑框里写下了代码也是有异常抛出的,这是怎么处理的呢?

    微软有开源代码编辑器比较受欢迎(VSCode的一部分):monaco-editor

    提示一下,如果真的要做在线编辑器,记得考虑一下fork炸弹,这个其实也是很老的东西了,程序员基本上都应该接触过了

    1.2.字符串和编码¶

    Python3.x版本中,字符串是以Unicode编码的

    对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符

    小潘对这块有所研究,把小明按在桌上然后抢先提交demo:

    In [13]:

    ord('D')
    

    Out[13]:

    68
    

    In [14]:

    ord('毒')
    

    Out[14]:

    27602
    

    In [15]:

    chr(68)
    

    Out[15]:

    'D'
    

    In [16]:

    chr(27602)
    

    Out[16]:

    '毒'
    

    In [17]:

    print(ord('A'))
    print(ord('Z'))
    
    print(ord('a'))
    print(ord('z'))
    

     

    65
    90
    97
    122
    

     

    老师补充讲解道:

    编码:encode() 解码:decode()

    url相关的可以用:

    urllib.parse.quote() and urllib.parse.unquote()

    urllib.parse.urlencode() 可以直接对一个key-value进行url编码

    In [18]:

    # encode() and decode()
    name="毒逆天"
    
    name_encode=name.encode("utf-8")
    
    print(name_encode)
    
    print(name_encode.decode("utf-8"))
    

     

    b'xe6xafx92xe9x80x86xe5xa4xa9'
    毒逆天
    

    In [19]:

    # 需要导入urlib.parse
    
    import urllib.parse
    

    In [20]:

    test_str="淡定"
    
    # 对字符串进行url编码和解码
    test_str_enode = urllib.parse.quote(test_str)
    
    print(test_str_enode)
    
    # urllib.parse.quote() 解码
    print(urllib.parse.unquote(test_str_enode))
    

     

    淡定
    淡定
    

    In [21]:

    # urlencode 可以直接对一个key-value进行编码
    
    test_dict={"name":"毒逆天","age":23}
    
    encode_str = urllib.parse.urlencode(test_dict)
    
    print(encode_str)
    print(urllib.parse.unquote(encode_str))
    

     

    name=毒逆天&age=23
    name=毒逆天&age=23
    

     

    3.1.Python ¶

    说多态之前说说类型判断,以前我们用 type() or isinstance()

    判断一个变量和另一个变量是否是同一个类型==> type(a)==type(b)

    判断一个变量是否是某个类型==> type(a)==A or isinstance(a,A)

    In [7]:

    # 判断一个变量是否是某个类型 ==> isinstance() or type
    class Animal(object):
        pass
    
    
    class Dog(Animal):
        pass
    
    
    def main():
        dog = Dog()
        dog2 = Dog()
        print(type(dog) == Dog)
        print(type(dog) == type(dog2))
        print(type(dog))
    
        print(isinstance(dog, Dog))
        print(isinstance(dog, Animal))
        # arg 2 must be a type or tuple
        # print(isinstance(dog, dog2))
    
    
    if __name__ == '__main__':
        main()
    

     

    True
    True
    <class '__main__.Dog'>
    True
    True
    

     

    小明老高兴了,终于讲解多态了,不禁问道:“ 多态的好处是啥?”

    小潘瞥了一眼小明~“废话,肯定为了 屏蔽子类差异用的啊,像简单工厂不就干的这个事?"

    小明楞了楞,眼巴巴的看着老师继续讲课。

    设计模式我们会找个专题讲讲,现在给你们说的是Python的基础。

    Python是动态语言的“ 鸭子类型”,它并不要求严格的继承体系。

    一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子(最后会贴一个案例)

    C#实现多态有很多方式,比如虚方法,比如抽象类,比如接口等等...

    小明迷迷糊糊的问道:“那 Python怎么实现多态呢?”

    老师看了一眼打断他讲课的小明,然后继续说道~来个简单案例:

    In [8]:

    class People(object):
        def eat(self):
            print("人类会吃饭")
    
    class Father(object):
        def eat(self):
            print("优雅的吃饭")
    
    class Teacher(object):
        def eat(self):
            print("赶时间的吃饭")
    
    # C# 或者 Java里面 写成 eat(People obj)
    def eat(obj):
        obj.eat()
    
    def main():
        teacher = Teacher()
        father = Father()
        eat(teacher)
        eat(father)
    
    if __name__ == '__main__':
        main()
    

     

    赶时间的吃饭
    优雅的吃饭
    

     

    多态的好处在于,如果这时候我再来个Son子类,有eat()方法编写正确,不用管原来的代码是如何调用的

    这次小明懂了,为了装一下,说道:”老师老师,我记得C# 或者 Java里面是写成 eat(People obj) 的吧?“

    老师欣慰的笑了一下,说道:”记得刚才说的 填鸭式吗?Python这么写有个好处哦,我们来看个案例,然后你自己总结“

    In [9]:

    class People(object):
        def eat(self):
            print("人类会吃饭")
    
    class Father(object):
        def eat(self):
            print("优雅的吃饭")
    
    class Teacher(object):
        def eat(self):
            print("赶时间的吃饭")
    
    class Dog(object):
        def eat(self):
            print("舔着吃")
    
    def eat(obj):
        obj.eat()
    
    def main():
        teacher = Teacher()
        father = Father()
        eat(teacher)
        eat(father)
    
        # 我们添加一个不是People子类的Dog类,只要有eat方法,参数一样就可以直接调
        dog = Dog()
        eat(dog)
    
    if __name__ == '__main__':
        main()
    

     

    赶时间的吃饭
    优雅的吃饭
    舔着吃
    

     

    小明突然大声说道:”老师老师,我知道了,Python这是吧类的继承和接口继承融合起来了啊,实现多态就相当于C#里面的接口实现多态啊!!!“

    老师点评道:”你姑且可以这么理解,这些我们后面还会继续说的,这种填鸭式的手段刚开始的确会有点不方便,用着用着你就会觉得挺方便的“


    小明认真思考总结,然后对照Python和小潘一起写下了 C#版的多态

    In [2]:

    1.5 抛出异常¶

    我们继续,像C#是用thorw抛出异常,那Python怎么 捕获异常后再抛出 呢?怎么自定义异常 呢?

    继续往下看:

    In [7]:

    # 捕获异常后再丢出,eg:在线运行的用户Code
    def main():
        try:
            1 / 0  # ZeroDivisionError: division by zero
        except ZeroDivisionError as ex:
            print(ex)  # 写个日志,回头出问题可以深究
            raise
    
    
    if __name__ == '__main__':
        main()
    

     

    division by zero
    

     

    ---------------------------------------------------------------------------
    ZeroDivisionError                         Traceback (most recent call last)
    <ipython-input-7-15f01346e2d8> in <module>()
          9 
         10 if __name__ == '__main__':
    ---> 11main()
    
    <ipython-input-7-15f01346e2d8> in main()
          2 def main():
          3     try:
    ----> 41 / 0  # ZeroDivisionError: division by zero
          5     except ZeroDivisionError as ex:
          6         print(ex)  # 写个日志,回头出问题可以深究
    
    ZeroDivisionError: division by zero
    

    In [8]:

    # 抛出自定义异常
    class DntException(BaseException):
        pass
    
    
    def get_age(num):
        if num <= 0:
            raise DntException("num must>0")
        else:
            print(num)
    
    
    def main():
        get_age(-1)
        get_age(22)  # 程序崩了,这句话不会被执行了
    
    
    if __name__ == '__main__':
        main()
    

     

    ---------------------------------------------------------------------------
    DntException                              Traceback (most recent call last)
    <ipython-input-8-7c9dec6ec225> in <module>()
         17 
         18 if __name__ == '__main__':
    ---> 19main()
    
    <ipython-input-8-7c9dec6ec225> in main()
         12 
         13 def main():
    ---> 14get_age(-1)
         15     get_age(22)  # 程序崩了,这句话不会被执行了
         16 
    
    <ipython-input-8-7c9dec6ec225> in get_age(num)
          6 def get_age(num):
          7     if num <= 0:
    ----> 8raise DntException("num must>0")
          9     else:
         10         print(num)
    
    DntException: num must>0
    

     

    异常这一块基本上讲完了(logging模块后面会说)有什么补充的可以说的^_^

     

    1.3.值判断和地址判断¶

    小明不乐意了,你个小潘总是抢我的风头,看完标题就刷刷的在黑板上写下了如下知识点:

    is 是比较两个引用是否指向了同一个对象id()得到的地址一样则相同)

    == 是比较两个对象的值是否相等

    在之前讲Dict的时候提了一下可变和不可变类型:https://www.cnblogs.com/dotnetcrazy/p/9155310.html#5.2.增加和修改

    Func里面又系统的说了一下:https://www.cnblogs.com/dotnetcrazy/p/9175950.html#4.5.可变类型和不可变类型

    对于可变不可变系列就不去复述了,下面再来几个案例看看 值判断地址判断的概念

    In [22]:

    ################ 可变类型 ################ 
    

    In [23]:

    a=[1,2,3]
    b=[1,2,3]
    
    # id不一样,那is肯定不一样了
    print(id(a))
    print(id(b))
    

     

    139727165899464
    139727165725256
    

    In [24]:

    # a和b是否指向同一个地址
    a is b
    

    Out[24]:

    False
    

    In [25]:

    # a和b的值是否相同
    a == b
    

    Out[25]:

    True
    

    In [26]:

    ################ 开始变化了 ################ 
    

    In [27]:

    # 让a指向b的地址
    a=b
    
    # a和b的id一样了
    print(id(a))
    print(id(b))
    

     

    139727165725256
    139727165725256
    

    In [28]:

    # a和b是否指向同一个地址
    a is b
    

    Out[28]:

    True
    

    In [29]:

    # a和b的值是否相同
    a == b
    

    Out[29]:

    True
    

    In [30]:

    ################ 不可变类型 ################ 
    

    In [31]:

    a=1
    b=1
    
    # id一样
    print(id(a))
    print(id(b))
    

     

    94592578394656
    94592578394656
    

    In [32]:

    a is b
    

    Out[32]:

    True
    

    In [33]:

    a == b
    

    Out[33]:

    True
    

    In [34]:

    # 但是你要注意,不是所有不可变类型都这样的
    
    f1=1.2
    f2=1.2
    
    # 声明两个相同值的浮点型变量,查看它们的id,发现它们并不是指向同个内存地址(这点和int类型不同)
    print(id(f1))
    print(id(f2))
    

     

    139727217917024
    139727217917096
    

    In [35]:

    # 这个就不一样了
    # 这方面涉及Python内存管理机制,Python对int类型和较短的字符串进行了缓存
    # 无论声明多少个值相同的变量,实际上都指向同个内存地址,其他的就没这福利咯~
    
    f1 is f2
    

    Out[35]:

    False
    

    In [36]:

    f1 == f2
    

    Out[36]:

    True
    

     

    3.2.C#虚方法实现多态 ¶

    定义一个人类:

    public class Person
    {
        #region 字段 属性
        /// <summary>
        /// 姓名
        /// </summary>
        private string _name;
        public string Name
        {
            get
            {
                return _name;
            }
    
            set
            {
                _name = value;
            }
        }
        /// <summary>
        /// 性别
        /// </summary>
        private bool _gender;
        public bool Gender
        {
            get
            {
                return _gender;
            }
    
            set
            {
                _gender = value;
            }
        }
        /// <summary>
        /// 年龄
        /// </summary>
        public short Age { get; set; }
        #endregion
    
        #region 构造函数
        public Person() { }
        public Person(string name, bool gender)
        {
            this.Name = name;
            this.Gender = gender;
        }
        public Person(string name, bool gender, short age) : this(name, gender)
        {
            this.Age = age;
        }
        #endregion
    
        #region 方法
        /// <summary>
        /// 打招呼
        /// </summary>
        public virtual void SaiHi()
        {
            Console.WriteLine("我是一个人类!");
        }
        #endregion
    }
    

    定义一个女孩类:

    public class Gril : Person
    {
        #region 构造函数
        public Gril() { }
        public Gril(string name, bool gender) : base(name, gender) { }
        public Gril(string name, bool gender, short age) : base(name, gender, age) { }
        #endregion
    
        /// <summary>
        /// 重写父类方法
        /// </summary>
        public override void SaiHi()
        {
            string genderStr = Gender == true ? "男孩" : "女孩";
            Console.WriteLine($"你好,我叫{Name},今年{Age}岁了,我是一个腼腆的小{genderStr}");
        }
    }
    

    定义一个男孩类:

    public class Boy : Person
    {
        #region 构造函数
        public Boy() { }
        public Boy(string name, bool gender) : base(name, gender) { }
        public Boy(string name, bool gender, short age) : base(name, gender, age) { }
        #endregion
    
        //public void SaiHi()
        public override void SaiHi()
        {
            string genderStr = Gender == true ? "男孩" : "女孩";
            Console.WriteLine($"你好,我叫{Name},今年{Age}岁了,我是一个腼腆的小{genderStr}");
        }
    }
    

    调用:

    static void Main(string[] args)
    {
        Person[] persons = { new Person(), new Boy("铁锅", true, 13), new Gril("妞妞", false, 22) };
        foreach (var item in persons)
        {
            //看看item里面到底放的是什么
            Console.WriteLine(item.ToString());
            item.SaiHi();
            Console.WriteLine();
        }
    }
    

    结果:

    Polymorphism1.Person
    我是一个人类!
    Polymorphism1.Boy
    你好,我叫铁锅,今年13岁了,我是一个腼腆的小男孩
    Polymorphism1.Gril
    你好,我叫妞妞,今年22岁了,我是一个腼腆的小女孩
    

     


    # 画图
    plt.plot(x_list,y_list)
    # 设置X,Y的坐标区间(可以不设置,用默认显示方式)
    plt.axis([0,10,0,25])
    # 显示图片
    plt.show()
    

    1.6 C#异常¶

    小明又进行了C#的代码转换,怎么看都觉得还是C#简单啊,根本不用说啥,代码一贴就秒懂了。。。

    In [1]:

    %%script csharp
    try
    {
        Convert.ToInt32("mmd");
    }
    catch (Exception ex)
    {
        // Input string was not in a correct format
        Console.WriteLine(ex.Message);
    }
    

     

    Input string was not in a correct format.
    

    In [2]:

    %%script csharp
    //抛出自定义异常
    try
    {
        throw new Exception("出错了啊");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    

     

    出错了啊
    

     

    你可以自定义异常类,继承Exception即可,对了C#里面也是有finally的

    try
    {
        throw new Exception("出错了啊");
        //Convert.ToInt32("mmd");
    }
    catch (Exception ex)
    {
        // Input string was not in a correct format
        Console.WriteLine(ex.Message);
    }
    finally
    {
        Console.WriteLine("finally");
    }
    

    现在一些需要finally的地方基本上都被using(){}接管了,所以特定场景会使用

    先这样了

    2.Python总结之for系列¶

    老师徐徐道来:“之前说for总是零零散散的,现在基础都讲完了,来个小汇总:”

    3.3.C#抽象类实现多态 ¶

    定义一个动物类:

    public abstract class Animal
    {
        /// <summary>
        /// 抽象类中可以有正常的方法
        /// </summary>
        public void Action()
        {
            Console.WriteLine("动物可以动");
        }
    
        /// <summary>
        /// 抽象方法必须在抽象类中
        /// </summary>
        public abstract void Call();
    }
    

    定义一个猫科动物类(子类必须实现父类抽象方法,如果不实现,那么该类也必须是抽象类)

    /// <summary>
    /// 猫科动物---子类必须实现父类抽象方法,如果不实现,那么该类也必须是抽象类
    /// </summary>
    public abstract class Feline : Animal
    {
    }
    

    定义一个猫类

    public class Cat : Feline
    {
        /// <summary>
        /// 子类必须实现父类抽象方法,如果不实现,那么该类也必须是抽象类
        /// </summary>
        public override void Call()
        {
            Console.WriteLine("喵喵叫~~~");
        }
    }
    

    定义一个狗类

    public class Dog : Animal
    {
        /// <summary>
        /// 子类必须实现抽象类中的抽象方法
        /// </summary>
        public override void Call()
        {
            Console.WriteLine("汪汪叫~~~");
        }
    }
    

    调用:

    Animal[] animals = { new Dog(), new Cat() };
    foreach (var item in animals)
    {
        item.Call();
    }
    

    结果:

    汪汪叫~~~
    喵喵叫~~~
    

     


     

    2.1.Base¶

    能够被for循环遍历的,就是可迭代的

    For基础系:

    In [37]:

    # 类似于for(int i=0;i<5;i  )
    
    for i in range(5):
        print(i)
    

     

    0
    1
    2
    3
    4
    

    In [38]:

    #while循环一般通过数值是否满足来确定循环的条件
    #for循环一般是对能保存多个数据的变量,进行遍历
    
    name="https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ#mmd"
    
    for i in name:
        if i=='#':
            break
        print(i,end='')#另一种写法:print("%s"%i,end="")
    print('n end ...')
    

     

    https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ
     end ...
    

    In [39]:

    # 你期望的结果是:i = 5
    
    for i in range(10):
        if i == 5:
            print("i = %d" % i)
    else:
        print("没有找到")
    

     

    i = 5
    没有找到
    

    In [40]:

    # 当迭代的对象迭代完并为空时,位于else的子句将执行
    # 而如果在for循环中含有break时则直接终止循环,并不会执行else子句
    # 正确写法如下:
    
    for i in range(10):
        if i == 5:
            print("i = %d" % i)
            break
    else:
        print("没有找到")
    

     

    i = 5
    

    In [41]:

    # 遍历一个字典
    
    test_dict={"Name":"小明","Age":23}
    
    for k,v in test_dict.items():
        print("key:%s,value:%s"%(k,v))
    

     

    key:Name,value:小明
    key:Age,value:23
    

     

    3.4.C#接口实现多态 ¶

    定义一个跑的接口:

    public interface IRun
    {
        /// <summary>
        /// 接口中可以声明属性,方法,索引器等
        /// </summary>
        //string Name { get; set; }
    
        void Runing();
    }
    

    定义一个猫类:

    public class Cat : IRun
    {
        public void Runing()
        {
            Console.WriteLine("飞快的跑着上树");
        }
    }
    

    定义一个学生类:

    public class Student : IRun
    {
        public void Runing()
        {
            Console.WriteLine("飞快的跑着去上课");
        }
    }
    

    调用:

    IRun[] objs = { new Student(), new Cat() };
    foreach (var item in objs)
    {
        item.Runing();
    }
    

    结果:

    飞快的跑着去上课
    飞快的跑着上树
    

    新葡亰496net 2

    2.2.列表生成式¶

    如果下面知识点还不熟悉的,看看之前讲的~列表生成式:https://www.cnblogs.com/dotnetcrazy/p/9155310.html#1.9.列表生成式

    简写list(range(1, 11)) 全写[x for x in range(1,11)]

    In [42]:

    list(range(1, 11))
    

    Out[42]:

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    In [43]:

    [x for x in range(1,11)]
    

    Out[43]:

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    In [44]:

    # 1~10的平方列表
    [x*x for x in range(1,11)]
    

    Out[44]:

    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
    

    In [45]:

    # 1~10之间的偶数
    [x for x in range(1, 11) if x % 2 == 0]
    

    Out[45]:

    [2, 4, 6, 8, 10]
    

    In [46]:

    # 数学里面的全排列
    [x   y for x in 'ABC' for y in 'AB']
    

    Out[46]:

    ['AA', 'AB', 'BA', 'BB', 'CA', 'CB']
    

    In [47]:

    # 数学里面的坐标轴
    [(x,y) for x in range(1,5) for y in range(1,4)]
    

    Out[47]:

    [(1, 1),
     (1, 2),
     (1, 3),
     (2, 1),
     (2, 2),
     (2, 3),
     (3, 1),
     (3, 2),
     (3, 3),
     (4, 1),
     (4, 2),
     (4, 3)]
    

    In [48]:

    # (x,y,z) 一般三个嵌套就上天了
    [(x,y,z) for x in range(1,5) for y in range(1,4) for z in range(1,3)]
    

    Out[48]:

    [(1, 1, 1),
     (1, 1, 2),
     (1, 2, 1),
     (1, 2, 2),
     (1, 3, 1),
     (1, 3, 2),
     (2, 1, 1),
     (2, 1, 2),
     (2, 2, 1),
     (2, 2, 2),
     (2, 3, 1),
     (2, 3, 2),
     (3, 1, 1),
     (3, 1, 2),
     (3, 2, 1),
     (3, 2, 2),
     (3, 3, 1),
     (3, 3, 2),
     (4, 1, 1),
     (4, 1, 2),
     (4, 2, 1),
     (4, 2, 2),
     (4, 3, 1),
     (4, 3, 2)]
    

     

     

    2.3.扩展¶

    如果要对list实现类似C#或者java那样的下标循环怎么办?

    这块小明又有预习,于是在提交Code的同时大声说道:

    Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身

    In [49]:

    for i, item in enumerate(['A', 'B', 'C']):
        print(i, item)
    

     

    0 A
    1 B
    2 C
    

     

    小潘昨晚螃蟹吃多了,今天拉肚子拉的不要不要的,现在去药店买紫苏和干姜来解蟹毒呢

    3.Python中赋值、浅拷贝、深拷贝¶

    看到标题小明和小潘就楞了,老师当时没讲解啊,然后两个人眼巴巴的看着老师讲解:

    官方文档:

    怎么办呢,感觉自己不再萌萌哒了,好心情瞬间被比下去了,小明内心不断思索中....

    3.1.赋值¶

    通过=来实现,就是把地址拷贝了一份,比如 a = b

    In [50]:

    a=[1,2,2]
    b = a
    
    print(id(a))
    print(id(b))
    

     

    139727165518536
    139727165518536
    

    In [51]:

    # 再验证
    
    a.append(3)
    
    # 都增加了一个3,说明的确指向同一个内存地址
    print(a)
    print(b)
    

     

    [1, 2, 2, 3]
    [1, 2, 2, 3]
    

     

    作为一个吃胖了的小明,过了一分钟就调节好了,然后好好看小潘的Code

    3.2.深拷贝deepcopy¶

    导入copy模块,调用deepcopy方法

    如果有嵌套引用的情况,直接递归拷贝

    In [52]:

    import copy
    
    a=[1,2,2]
    

    In [53]:

    b=copy.deepcopy(a)
    
    # 指向了不同的内存地址
    print(id(a))
    print(id(b))
    

     

    139727165899080
    139727165900488
    

    In [54]:

    # 再验证一下
    
    a.append(3)
    
    # b不变,说明的确指向不同的内存地址
    print(a)
    print(b)
    

     

    [1, 2, 2, 3]
    [1, 2, 2]
    

    In [55]:

    ################ 开始变化了 ################ 
    

    In [56]:

    # 之前讲了嵌套列表,我们来验证一下
    
    a=[1,2,2]
    b=[1,2,3,a]
    
    c=copy.deepcopy(b)
    
    # 发现地址都不一样
    print(id(b))
    print(id(c))
    print(id(b[3]))
    print(id(c[3]))
    

     

    139727166586248
    139727165899080
    139727165725256
    139727165899464
    

    In [57]:

    # 直观的验证一下
    
    a.append(666)
    
    # 深拷贝的确是深拷贝
    print(b)
    print(c)
    

    新葡亰496net:继承与多态,面向对象之。 

    [1, 2, 3, [1, 2, 2, 666]]
    [1, 2, 3, [1, 2, 2]]
    

     

    咦,第一句是啥?不管,继续看x_list=list(range(1,11)) y_list=[y*2 1 for y in x_list]

    3.3.浅拷贝copy¶

    copy只是简单拷贝,如果拷贝内容里面还有引用之类的,他是不管的

    In [58]:

    import copy
    
    a=[1,2,2]
    

    In [59]:

    b=copy.copy(a)
    
    # 指向了不同的内存地址
    print(id(a))
    print(id(b))
    

     

    139727165902088
    139727165850952
    

    In [60]:

    ################ 开始变化了 ################ 
    

    In [61]:

    # 之前讲了嵌套列表,我们来验证一下
    
    a=[1,2,2]
    b=[1,2,3,a]
    
    c=copy.copy(b)
    
    # 第一层地址不一样
    print(id(b))
    print(id(c))
    

     

    139727165519432
    139727165902088
    

    In [62]:

    # 验证一下
    b.append(111)
    
    # 第一层指向的不同地址
    print(b)
    print(c)
    

     

    [1, 2, 3, [1, 2, 2], 111]
    [1, 2, 3, [1, 2, 2]]
    

    In [63]:

    # 如果里面还有引用,那么就不管了
    print(id(b[3]))
    print(id(c[3]))
    

     

    139727165725576
    139727165725576
    

    In [64]:

    # 验证一下
    a.append(666)
    
    # 内部引用的确没copy新地址
    print(b)
    print(c)
    

     

    [1, 2, 3, [1, 2, 2, 666], 111]
    [1, 2, 3, [1, 2, 2, 666]]
    

     

    这不是之前老师讲的列表生成式吗?然后就是画图了,好像也不是很难,开头到底是个啥,有了这个就可以几句话实现牛逼哄哄的技能了,不行得买瓶饮料问问课代表伟哥~

    3.4.知识扩展¶

    如果拷贝的对象是不可变类型,不管深拷贝和浅拷贝以及赋值都是地址引用

    需要注意的是:Python和Net对于值类型处理是不一样的(管理方式不一样导致的)

    ==>NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝

    In [65]:

    a=(1,2,2)
    b=a
    
    print(id(a))
    print(id(b))
    

     

    139727165526520
    139727165526520
    

    In [66]:

    a=(1,2,2)
    b=copy.deepcopy(a)
    
    print(id(a))
    print(id(b))
    

     

    139727165846872
    139727165846872
    

    In [67]:

    a=(1,2,2)
    b=copy.copy(a)
    
    print(id(a))
    print(id(b))
    

     

    139727165526520
    139727165526520
    

     

    一进伟哥宿舍就发现,伟哥在给舍友们补课?这是要讲课的节奏吗?赶紧凑过来学习~

    4.CSharp中赋值、浅拷贝、深拷贝¶

    小明听懂了Python的深拷贝和浅拷贝后,本着学以致用的原则,写下了C#的实现:

    先声明一下,本机环境是Ubuntu NetCore,欢迎贴Code补充

    1.模块导入¶

    模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能

    在Python中,一个py文件就称之为一个 模块(Module)

    我们先来讲讲 模块怎么导入,上学期我们学了点C,发现每个文件开头都有一个#include <stdio.h>

    这个就类似我们说的模块导入了,咱们Python的模块导入和Java有点像用 import 来导入

    先看个例子 ~ 我们定义了一个test.py文件,里面定义了这么一个函数:

    # %load test.py
    def show():
        print("---show---")
    

    我们想在新的py文件中使用这个函数

    4.1.赋值¶

    Code:

    赋值方法和Python一样,直接赋值即可

    var list1 = new List<int>() { 1, 2, 2 };
    var list2 = list1;
    

    In [68]:

    %%script csharp
    
    // Python一样,直接赋值即可
    var list1 = new List<int>() { 1, 2, 2 };
    var list2 = list1;
    
    // 验证一下
    list1.Add(3);//我们修改一下list1,list2也就跟着就改变了
    
    foreach (var item in list1)
    {
        Console.Write(item   " ");
    }
    Console.WriteLine();
    foreach (var item in list2)
    {
        Console.Write(item   " ");
    }
    

     

    1 2 2 3 
    1 2 2 3 
    

     

    1.1.import导入方式1¶

    import test 把这个文件直接导入进来了

    import test
    
    test.show()
    

    执行结果:

    ---show---
    

    4.2值类型默认深拷贝¶

    NetCore深拷贝相关的官方文档 public void CopyTo (T[] array);

    简单类型用最简单的方式就能实现深拷贝了:

    官方的CopyTo在这里和这个效果一样,但是比较麻烦,这边就不贴了(Code里面贴了)

    var list3 = new List<int>() { 1, 2, 2 };
    var list4 = new List<int>(list3);
    
    // 验证一下
    list3.Add(3);
    foreach (var item in list3)
    {
        Console.Write(item   " ");
    }
    Console.WriteLine();
    foreach (var item in list4)
    {
        Console.Write(item   " ");
    }
    

    结果:

    1 2 2 3 
    1 2 2
    

    1.2.from…import导入方式2¶

    有时候我们只需要用到模块中的某个函数,只需要引入该函数即可

    from test import show 把这个文件的show方法导入进来,调用的时候直接show()即可

    from test import show
    
    show()
    

    执行结果:

    ---show---
    

    使用模块有什么好处?

    1. 提高了代码的维护性,当一个模块编写完毕,就可以被其他地方引用。

    2. 使用模块还可以 避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中

    我们自己在编写模块时,不必考虑名字会与其他模块冲突(不要和内置函数名字冲突)

    新葡亰496net 3

     

    4.3.引用类型默认浅拷贝¶

    对于List<T>再复杂点的,上面的方式就变成浅拷贝了:(类似于Python的Copy.Copy)

    官方的CopyTo在这里和这个效果一样,但是比较麻烦,这边就不贴了(Demo里面贴了)

    定义一个Student

    public partial class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        public override string ToString()
        {
            return $"Name:{Name},Age:{Age}";
        }
    }
    

    浅拷贝Demo:

    var list5 = new List<Student>(){
        new Student { Name = "小张", Age = 22 },
        new Student { Name = "小明", Age = 23 }
        };
    var p = new Student() { Name = "小潘", Age = 23 };
    list5.Add(p);
    
    // 浅拷贝一份
    var list6 = new List<Student>(list5);
    
    // 浅拷贝测试
    // 我们修改一下list5,list6没有跟着改变,说明第一层的地址的确不一样
    list5.Add(new Student() { Name = "小胖", Age = 24 });
    // 当我们修改小潘同学的年龄时,大家都变了,说明真的只是浅拷贝
    p.Age = 24;
    
    foreach (var item in list5)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("=============");
    foreach (var item in list6)
    {
        Console.WriteLine(item);
    }
    

    结果:

    Name:小张,Age:22
    Name:小明,Age:23
    Name:小潘,Age:24
    Name:小胖,Age:24
    =============
    Name:小张,Age:22
    Name:小明,Age:23
    Name:小潘,Age:24
    

     

    1.3.导入多个模块¶

    看着小明一愣一愣的,加上那瓶菊花茶的面子,课代表来了一个demo:

    test1.py

    test_str = "111"
    
    def show():
        print(__name__, "show")
    

    test2.py

    test_str = "222"
    
    def show():
        print(__name__, "show")
    

    可以以,分隔(这种导入方式,官方推荐多行导入

    import test1, test2
    
    print(test1.test_str)
    
    print(test2.test_str)
    
    test1.show()
    
    test2.show()
    

    结果:

    111
    222
    test1 show
    test2 show
    

    还可以用这种方式,该模块里面的函数名、变量名以逗号分隔(不用多行)

    通过这种方式引入的时候,是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入,所以用as来避免一下

    from test1 import show as show1, test_str as test_str1
    from test2 import show as show2, test_str as test_str2
    
    print(test_str1)
    print(test_str2)
    
    show1()
    show2()
    

    结果:

    111
    222
    test1 show
    test2 show
    

    如果想引入模块中所有东西可以用 from 模块名 import *,这样所有被允许(过会讲)的变量和函数都被导入进来了(少用)

    from 模块名 import 好处:使用起来方便了,坏处:容易与当前执行文件中的名字冲突

     

    4.4.简单方式实现深拷贝¶

    对于List<T>的深拷贝场景,其实项目中还是蛮常见的,那深拷贝怎么搞呢?

    先来一个简单的实现方式,需要T实现ICloneable接口才行:

    定义一个Person类

    public partial class Person : ICloneable
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        //实现ICloneable的Clone方法
        public object Clone()
        {
            return base.MemberwiseClone();//调用父类方法即可
        }
    
        public override string ToString()
        {
            return $"Name:{Name},Age:{Age}";
        }
    }
    

    List<T>定义一个扩展方法:(温馨提醒:扩展方法所在的类必须是static Class哦)

    public static partial class ListExt
    {
        // 只要T实现了ICloneable接口就可以了
        public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> list) where T : ICloneable
        {
            return list.Select(item => (T)item.Clone()).ToList();
        }
    }
    

    来个调用加验证:

    #region 引用类型深拷贝-简单实现方式
    
    var oldList = new List<Person>(){
        new Person(){Name="小明",Age=23},
        new Person(){Name="小张",Age=22},
    };
    var xiaoPan = new Person() { Name = "小潘", Age = 23 };
    oldList.Add(xiaoPan);
    
    var newList = oldList.DeepCopy();
    
    //测试
    oldList.Add(new Person() { Name = "小胖", Age = 23 });
    xiaoPan.Age = 24;
    
    foreach (var item in oldList)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("========");
    foreach (var item in newList)
    {
        Console.WriteLine(item);
    }
    
    #endregion
    

    结果:

    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:24
    Name:小胖,Age:23
    ========
    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:23
    

     

    2.模块的简单自测试¶

    小明恍然大悟,盯着__name__看了好久,然后结合前面的知识,乘着旁边位置电脑没人,写了一半的demo卡壳了:

    get_infos.py

    def get_infos():
        print("信息如下:xxxx")
    
    def main():
        get_infos()
    
    if __name__ == '__main__':
        main()
    

    伟哥过来瞟了一眼继续讲到:

    之前老师讲面向对象的时候有提到标准格式写法:

    def main():
        get_infos()
    
    if __name__ == '__main__':
        main()
    

    python文件可以有两种用途:

    1. 当做 脚本来单独使用或者执行
    2. 当做 模块来给别人调用

    那怎么区分呢?就要用到__name__

    我们来看个案例就懂了:

    get_user_infos.py

    def get_infos():
        print(__name__)
        print("信息如下:xxxx")
    
    def main():
        get_infos()
    
    if __name__ == '__main__':
        main()
    

    再定义一个函数来调用它

    import get_user_infos as user_infos
    
    user_infos.get_infos()
    

    直接执行的时候__name__等于__main__,别人调用的时候显示该文件名 新葡亰496net 4

    这样的好处就是==> 避免被别人导入的时候执行某些Code

    小明乐坏了,平时和小潘以前做项目老被吐槽,学了这个得好好嘲讽一下小潘了

    以后再也不怕直接py文件各种问题了,咱们可以先简单自测一下O(∩_∩)O哈!

     

    4.5.序列化方式实现深拷贝(常用)¶

    利用System.Runtime.Serialization序列化与反序列化实现深拷贝

    先定义一个Teacher类(别忘记加 Serializable 的标签)

    [Serializable]
    public partial class Teacher
    {
        public string Name { get; set; }
        public int Age { get; set; }
    
        public override string ToString()
        {
            return $"Name:{Name},Age:{Age}";
        }
    }
    

    添加一个扩展方法:

    public static partial class ListExt
    {
        // 利用System.Runtime.Serialization序列化与反序列化实现深拷贝
        public static T DeepCopy2<T>(this T obj)
        {
            using (var stream = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
    }
    

    调用:

    #region 引用类型深拷贝-序列化实现
    
    var oldTestList = new List<Teacher>(){
        new Teacher(){Name="小明",Age=23},
        new Teacher(){Name="小张",Age=22},
    };
    var s = new Teacher() { Name = "小潘", Age = 23 };
    oldTestList.Add(s);
    
    var newTestList = oldTestList.DeepCopy2();
    
    //测试
    oldTestList.Add(new Teacher() { Name = "小胖", Age = 23 });
    s.Age = 24;
    
    foreach (var item in oldTestList)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("========");
    foreach (var item in newTestList)
    {
        Console.WriteLine(item);
    }
    
    #endregion
    

    结果:

    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:24
    Name:小胖,Age:23
    ========
    Name:小明,Age:23
    Name:小张,Age:22
    Name:小潘,Age:23
    

    因为主要是说Python,Net只是简单提一下,这边就先到这里了

    不尽兴可以看看这篇文章,讲得还是挺全面的

    我们接着来对比学习~

     

    3.控制可使用的函数、变量、类¶

    5.Python生成器¶

    一看到标题小明又懵圈了,但是看到大家好像都知道的样子心想道:“我是不是又睡过一节课啊?”

    之前有讲列表生成式,这边说说生成器

    通过列表生成式,我们可以简单并直接的创建一个列表,但是当数据有一定的规律而且又很大的时候,使用列表就有点浪费资源了

    如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,从而节省大量的资源

    3.1.通过私有来控制¶

    在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的

    小明同学基础功底扎实,看见标题小明就乐了,前面看了这么多案例瞬间秒懂,大叫道~”等一下~~~我来写demo,要是不行请你们吃晚饭~“

    写下了如下代码:

    test3.py

    def test1():
        print("test1")
    
    
    def _test2():
        print("test2")
    
    
    def __test3():
        print("test3")
    

    调用代码:

    import test3
    
    test3.test1()
    test3._test2()
    test3.__test3()
    

    结果:

    test1
    test2
    test3
    

    大家大眼瞪小眼的看着小明,嘴角边露出了阴谋的味道~

    这下小明傻眼了,难道老师之前类的私有系列讲错了?

    小平瞥了小明一眼说道,你自己的打开方式不对还怪老师咯?看我给你改改~

    调用代码:

    from test3 import *
    
    test1()
    _test2()
    __test3()
    

    小明运行后就傻眼了,心想到,课代表这个宿舍卧虎藏龙啊,下次少来点,不然这点老底就被他们吃空了

    NameError: name '_test2' is not defined

    注释掉_test2()运行结果: NameError: name '__test3' is not defined

    小明有点尴尬,然后转了个话题,说道:”那import不是无敌了?我们的私有不给力啊?“

    伟哥语重心长的说道:

    类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用

    之所以我们说,“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量

    比如小明的那种方式就可以直接使用了,但是从编程习惯上不应该引用private函数或变量。

    我们平时使用的时候:

    外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public,然后导入的时候尽量使用from ... import

    5.1.简单方式¶

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

    先看一个简单的生成器案例:(只要把一个列表生成式的[]改成() ,就创建了一个generator了)

    In [69]:

    # 列表生成式
    [x for x in range(10)]
    

    Out[69]:

    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    In [70]:

    # 生成器写法(Python2.x系列是用xrange)
    (x for x in range(10))
    

    Out[70]:

    <generator object <genexpr> at 0x7f14c413cb48>
    

     

    遍历方式可以用之前的for循环来遍历(推荐)

    也可以用next()或者__next__()方法来遍历。【C#是用MoveNext

    generator保存的是算法,每次调用next(xxx)或者__next__(),就计算出下一个元素的值,直到计算到最后一个元素

    当没有更多的元素时,抛出StopIteration的异常

    最新的Python3.7在这方面有所优化:

    In [71]:

    g=(x for x in range(10))
    
    # for来遍历(推荐)
    for i in g:
        print(i)
    

     

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    

    In [72]:

    g=(x for x in range(10))
    
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(g.__next__()) #通过__next__也一样取下一个
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    

     

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    

     

    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-72-9897a9148994> in <module>()
         11 print(next(g))
         12 print(next(g))
    ---> 13print(next(g))
         14 print(next(g))
    
    StopIteration: 
    

     

    3.2.通过__all__来控制¶

    来个案例即可:

    test4.py

    __all__ = ["Dog", "test1", "name"]
    
    class Animal(object):
        pass
    
    class Dog(Animal):
        pass
    
    class Cat(Animal):
        pass
    
    def test1():
        print("test1")
    
    def test2():
        print("test2")
    
    def test3():
        print("test3")
    
    name = "小明"
    age = "22"
    

    效果:只有__all__列表里面的才能被访问 新葡亰496net 5

     

    5.2.yield方式¶

    如果推算的算法比较复杂,用类似列表生成式的for循环无法实现时,还可以用函数来实现

    这时候就需要用到yield了,像最经典的斐波拉契数列,这次用一波生成器来对比实现下:

    In [73]:

    # 递归方式:求第30个数是多少
    
    # 1、1、2、3、5、8、13、21、34...
    def fib(n):
        if n == 1 or n == 2:
            return 1
        else:
            return fib(n - 1)   fib(n - 2)
    
    fib(30)
    

    Out[73]:

    832040
    

    In [74]:

    # 在讲yield方式之前先用循环实现一下
    
    def fibona(max):
        n, a, b = 0, 0, 1
        while n < max:
            print(b)
            a, b = b, a   b
            n = n   1
    
    fibona(30)
    

     

    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89
    144
    233
    377
    610
    987
    1597
    2584
    4181
    6765
    10946
    17711
    28657
    46368
    75025
    121393
    196418
    317811
    514229
    832040
    

    In [75]:

    # for循环实现
    
    def fibona(n):
        a, b = 0, 1
        # [0,n)
        for i in range(n):
            print(b)
            a, b = b, a   b
    
    fibona(30)
    

     

    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89
    144
    233
    377
    610
    987
    1597
    2584
    4181
    6765
    10946
    17711
    28657
    46368
    75025
    121393
    196418
    317811
    514229
    832040
    

     

    a, b = b, a b 之前交换两数的时候提过

    这个相当于==>

    temp_tuple = (b, a   b)
    a = temp_tuple[0]
    b = temp_tuple[1]
    

    要把fibona函数变成generator,只需要把print(b)改为yield b就可以了:

    generator在执行过程中,遇到yield就中断,下次又继续执行到yield停下了,一直到最后

    生成器的特点:

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

    In [76]:

    # 改成生成器比较简单,直接换输出为yield
    
    def fibona(n):
        a, b = 0, 1
        # [0,n)
        for i in range(n):
            yield b
            a, b = b, a   b
    

    In [77]:

    # 看看是不是生成器
    g = fibona(30)
    
    g
    

    Out[77]:

    <generator object fibona at 0x7f14c40efd58>
    

    In [78]:

    # 遍历输出(基本上都会用for来遍历)
    for i in g:
        print(i)
    

     

    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89
    144
    233
    377
    610
    987
    1597
    2584
    4181
    6765
    10946
    17711
    28657
    46368
    75025
    121393
    196418
    317811
    514229
    832040
    

     

    对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的循环的时候

    小明总结如下:

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

    2. 每一个生成器都是一个迭代器(迭代器不一定是生成器)

    3. 如果一个函数包含yield关键字,这个函数就会变为一个生成器

    4. 生成器并不会一次返回所有结果,而是每次遇到yield关键字后返回相应结果,并保留函数当前的运行状态,等待下一次的调用

    5. 由于生成器也是一个迭代器,那么它就支持next用方法来获取下一个值(我们平时用for来遍历它)

    推荐一篇文章,总结的很全了:(yield用法总结)

    4.二进制文件pyc¶

    小明,看到使用的电脑出现了这么多的pyc文件,以为中毒了,慌了慌了,连忙问道:"伟哥伟哥,这个pyc是啥啊?不会是病毒吧?"

    ├── 1.def_module.py
    ├── 2.use_module.py
    ├── 3.use_name.py
    ├── 4.use_module.py
    ├── 5.use_all_.py
    ├── get_user_infos.py
    ├── __pycache__
    │   ├── get_user_infos.cpython-36.pyc
    │   ├── test1.cpython-36.pyc
    │   ├── test2.cpython-36.pyc
    │   ├── test3.cpython-36.pyc
    │   ├── test4.cpython-36.pyc
    │   └── test.cpython-36.pyc
    ├── test1.py
    ├── test2.py
    ├── test3.py
    ├── test4.py
    └── test.py
    

    旁边小李嘲笑道~”这你都不知道“

    为了 提高加载模块的速度,python解释器会在pycache目录中下缓存每个模块编译后的版本

    格式为:模块名.Python版本号.pyc 这种命名规范保证了编译后的结果多版本共存

    小明问道,那我更新文件它怎么办呢?

    小李瞥了一眼说道:

    Python检查源文件的修改时间与编译的版本进行对比,如果过期就自动重新编译

    并且编译的模块是平台独立的,所以相同的库可以在不同的架构的系统之间共享

    小明恍然大悟,说道:

    这不类似于Java和Net嘛,将来咋们编写的东西都可以跨平台了啊,O(∩_∩)O~,反正是由python虚拟机来执行的,太方便了

    PS:pyc文件是可以反编译的,因而它的出现仅仅是用来提升模块的加载速度的,不是用来加密的

     

    5.3.扩展之~send(msg)方法:¶方法:)

    其实__next__()send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去

    __next__()不 能传递特定的值。我们可以看做x.__next__()x.send(None) 作用是一样的

    In [79]:

    # 来个案例:
    def test_send(n):
        for i in range(n):
            tmp = yield i
            print(tmp)
    
    
    g = test_send(5)
    
    g
    

    Out[79]:

    <generator object test_send at 0x7f14c40efdb0>
    

    In [80]:

    # 定义一个列表
    test_list = []
    
    # 把第一次yield的值放在列表中
    test_list.append(g.__next__())
    
    # 把list传给tmp并打印(可以理解为把表达式右边的 yield i 暂时换成了 test_list)
    # out的内容是yield返回的值
    g.send(test_list)
    

     

    [0]
    

    Out[80]:

    1
    

    In [81]:

    # 以防你们看不懂,来个简单案例
    # 你传啥print(tmp)就给你打印啥
    g.send("你好啊")
    

     

    你好啊
    

    Out[81]:

    2
    

     

    注意一种情况,generator刚启动的时候,要么不传,要么只能传None

    解决:要么一开始send(None)要么一开始先调用一下__next()__ or next()

    In [82]:

    # 注意一种情况,generator刚启动的时候,要么不传,要么只能传None
    def test_send(n):
        for i in range(n):
            tmp = yield i
            print(tmp)
    
    
    g = test_send(5)
    g.send("dog") # TypeError: can't send non-None value to a just-started generator
    

     

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-82-2e891aa5dd81> in <module>()
          7 
          8 g = test_send(5)
    ----> 9g.send("dog") # TypeError: can't send non-None value to a just-started generator
    
    TypeError: can't send non-None value to a just-started generator
    

    In [83]:

    # 解决:要么一开始send(None)要么一开始先调用一下__next()__ or next()
    def test_send(n):
        for i in range(n):
            tmp = yield i
            print(tmp)
    
    
    g = test_send(5)
    g.send(None)
    

    Out[83]:

    0
    

    In [84]:

    g.send("dog")
    

     

    dog
    

    Out[84]:

    1
    

     

    扩:C#在遍历generator的时候也是先调一下MoveNext方法

    while (tmp.MoveNext())
    {
        Console.WriteLine(tmp.Current);
    }
    

     

    5.自己添加模块路径¶

    小明乘着大家休息的时候回宿舍拿了个笔记本,继续听道:

    当你导入一个模块,Python解析器对模块位置的搜索顺序是:

    1. 当前目录
    2. 如果不在当前目录,Python则搜索在shell变量PYTHONPATH下的每个目录。
    3. 如果都找不到,Python会查看默认路径。xinux下,默认路径一般为/usr/local/lib/python/
    4. 模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录

    总之你看sys.path就对了

    前几天时间发布了Python3.7,我们设置vscode的运行环境为Python3.7,然后看看: 新葡亰496net 6

    我用的是conda的一个路径,Python3.7相关的一下插件没那么完善,我用的是3.6系列,3.7你可以用来练习一下新语法糖 新葡亰496net 7

    ['', # 当前目录
    '/usr/lib/python37.zip', '/usr/lib/python3.7', 
    '/usr/lib/python3.7/lib-dynload',
     '/usr/local/lib/python3.7/dist-packages', 
     '/usr/lib/python3/dist-packages']
    

    小明心想,我是省事直接装了个conda路径是啥呢?于是自己测试了下:

    In [3]:

    # conda的默认搜索路径
    import sys
    
    print(sys.path)
    

     

    ['', '/home/dnt/anaconda3/lib/python36.zip', '/home/dnt/anaconda3/lib/python3.6', '/home/dnt/anaconda3/lib/python3.6/lib-dynload', '/home/dnt/.local/lib/python3.6/site-packages', '/home/dnt/anaconda3/lib/python3.6/site-packages', '/home/dnt/anaconda3/lib/python3.6/site-packages/IPython/extensions', '/home/dnt/.ipython']
    

     

    运维方面用些许,平时用的不是很多,一带而过~

    细心可以看到,sys.path是一个列表,那么你对其添加删除操作也是和list一样的

    比如这个场景,服务器给不同的用户使用,又不想他们的程序访问到超过其文件夹外的文件(系统、其他用户)

    那么可以弄一个公共的库文件夹(手段先不用管),用程序模拟一下 新葡亰496net 8

    这种方法是在运行时修改,运行结束后失效

    其实还可以设置环境变量pythonpath,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。

    注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。(列表第二个就是说的这个)

    5.4.扩展之~returnbreak的说明¶

    在一个generator函数中,如果没有return则默认执行至函数完毕

    如果在执行过程中return或者break则直接抛出StopIteration终止迭代

    In [85]:

    # break案例
    def test_send(n):
        for i in range(n):
            if i==2:
                break
            yield i
    
    g = test_send(5)
    for i in g:
        print(i)
    

     

    0
    1
    

    In [86]:

    # return案例
    def test_send(n):
        for i in range(n):
            if i==2:
                return "i==2"
            yield i
    
    g = test_send(5)
    for i in g:
        print(i)
    

     

    0
    1
    

     

    for循环调用generator时,发现拿不到generatorreturn语句的返回值

    如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value

    In [87]:

    # 上面return的返回值怎么拿呢?
    
    g = test_send(5)
    
    while True:
        try:
            tmp = g.__next__()
            print(tmp)
        except StopIteration as ex:
            print(ex.value)
            break # 一定要加break,别忘了你在死循环里呢
    

     

    0
    1
    i==2
    

     

    6.模块重新导入¶

    import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次

    为了防止你重复导入,python的优化手段是:

    第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句

    看个例子: 新葡亰496net 9

    想要重新导入修改的模块,可以使用官方推荐的方法:importlib下面的reload方法

    from importlib import reload
    
    reload(test)
    

    当然了你也可以使用imp下面的reload方法(3.4往后,官方就不推荐使用 **imp** 了)

    5.5.扩展之~协程yield实现多任务调度¶

    这个场景还是很常见的,比如C#的单线程实现多任务用的就可以使用yield

    再比如生产消费这个经典案例:(参考)

    生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产

    Python对协程的支持是通过generator实现的

    在generator中,我们不但可以通过for循环来迭代,还可以不断调用__next__()获取由yield语句返回的下一个值。

    因为Python的yield不但可以返回一个值,它还可以接收调用者发出的参数(通过send方法),所以就happy了

    我们举个简单的demo来看看:

    In [88]:

    def consumer():
        while True:
            tmp = yield
            # !None就变成真了
            if not tmp:
                return
            print("消费者:",tmp)
    

    In [89]:

    # 创建消费者
    c = consumer()
    # 启动消费者
    c.send(None)
    # 生产数据,并提交给消费者
    c.send("小明")
    c.send("小潘")
    # 生产结束,通知消费者结束,抛出StopIteration异常
    c.send(None) # 使用c.close()可以避免异常
    

     

    消费者: 小明
    消费者: 小潘
    

     

    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-89-bcc0083d4089> in <module>()
          7 c.send("小潘")
          8 # 生产结束,通知消费者结束,抛出StopIteration异常
    ----> 9c.send(None) # 使用c.close()可以避免异常
    
    StopIteration: 
    

     

    执行流程

    1. 创建协程对象(消费者)后,必须使用send(None)__next__()启动
    2. 协程在执行yield后让出执行绪,等待消息
    3. 调用方发送send(msg)消息,协程恢复执行,将接收到的数据保存并执行后续流程
    4. 再次循环到yield,协程返回前面的处理结果,并再次让出执行绪
    5. 直到关闭或被引发异常

    补全demo:

    In [90]:

    def consumer():
        status = ""
        while True:
            tmp = yield status
            if not tmp:
                print("消费者已经睡觉了...")
                return
            print("消费者:获得商品%s号..." % tmp)
            status = "ok"
    
    
    def produce(c):
        # 启动消费者
        c.send(None)
        for i in range(1, 3):
            print("生产者:出产商品%s号..." % i)
            # 生产商品,并提交给消费者
            status = c.send(i)
            print("生产者:生产者消费状态: %s" % status)
        # c.send(None) 执行这个会引发StopIteration
        c.close()  # 使用close就可以避免了(手动关闭生成器函数,后面的调用会直接返回StopIteration异常)
    
    
    # 创建消费者
    c = consumer()
    produce(c)
    

     

    生产者:出产商品1号...
    消费者:获得商品1号...
    生产者:生产者消费状态: ok
    生产者:出产商品2号...
    消费者:获得商品2号...
    生产者:生产者消费状态: ok
    

    In [91]:

    # 更多可以查看帮助文档
    def test():
        yield
    help(test())
    

     

    Help on generator object:
    
    test = class generator(object)
     |  Methods defined here:
     |  
     |  __del__(...)
     |  
     |  __getattribute__(self, name, /)
     |      Return getattr(self, name).
     |  
     |  __iter__(self, /)
     |      Implement iter(self).
     |  
     |  __next__(self, /)
     |      Implement next(self).
     |  
     |  __repr__(self, /)
     |      Return repr(self).
     |  
     |  close(...)
     |      close() -> raise GeneratorExit inside generator.
     |  
     |  send(...)
     |      send(arg) -> send 'arg' into generator,
     |      return next yielded value or raise StopIteration.
     |  
     |  throw(...)
     |      throw(typ[,val[,tb]]) -> raise exception in generator,
     |      return next yielded value or raise StopIteration.
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |  
     |  gi_code
     |  
     |  gi_frame
     |  
     |  gi_running
     |  
     |  gi_yieldfrom
     |      object being iterated by yield from, or None
    

     

    6.1.循环导入的问题¶

    稍微提一下循环导入(类似于C#循环引用),能避免就避免。

    子模块相对独立,尽量别相互导入,通过父模块来调用 (有点像微服务的感觉)

    这样不会混乱也不会出现循环导入的问题了

    如果项目里面真的出现了循环导入,那么可以这么解决(参考):

    1. 重新设计架构,解除相互引用的关系
    2. 把import语句放置在模块的最后
    3. 把import语句放置在函数中

     

    6.Python迭代器¶

    看到迭代器小明老高兴了,心想着一会写个C#版的觉得可以收获一大群眼球~

    7.常用模块¶

    看到标题小明就乐了,这不~终于可以说话了,不用被他们压抑的不敢出声了~

    之前说了很多,比如time,random,sys等等,这次再扩充几个用的比较多的模块

    后面demo里面还会再说,可以自己学习一下(官方文档)(源码文件)

    6.1.判断是否可迭代¶

    在说迭代器前先说下可迭代(Iterable)(yield基础点我):

    在Python中,能通过for循环遍历的都是可以迭代的,比如 str、tuple、list、dict、set、生成器等等

    也可以通过 isinstance(xxx,Iterable) 方法判断一下是否迭代:

    In [92]:

    from collections import Iterable
    

    In [93]:

    isinstance("mmd",Iterable)
    

    Out[93]:

    True
    

    In [94]:

    isinstance((1,2),Iterable)
    

    Out[94]:

    True
    

    In [95]:

    isinstance([],Iterable)
    

    Out[95]:

    True
    

    In [96]:

    isinstance({},Iterable)
    

    Out[96]:

    True
    

    In [97]:

    isinstance((x for x in range(10)),Iterable)
    

    Out[97]:

    True
    

    In [98]:

    isinstance(1,Iterable)
    

    Out[98]:

    False
    

     

    7.1.time and datetime¶

    在Python中,通常有这几种方式来表示时间:

    由于Python的time模块实现主要调用C库,所以各个平台可能有所不同

    1. 时间戳(timestamp):从1970年1月1日00:00:00开始按秒计算的偏移量
      • type(time.time()) ==> float
      • 返回时间戳方式的函数主要有time()clock()(这个Win和Xinux系列有些区别)等
    2. 格式化的时间字符串
    3. UTC世界标准时间(在中国为UTC 8),DST(Daylight Saving Time)即夏令时。
      • 元组(struct_time)共有9个元素
      • 返回struct_time的函数主要有gmtime(),localtime(),strptime()

    看到表格不用怀疑,没错,更详细可以看官方介绍

    | 索引 | 属性                     | 值                        |
    | ---- | -------------------------| ------------------------- |
    | 0    | tm_year(年)             | eg:2018                   |
    | 1    | tm_mon(月)              | range [1, 12]             |
    | 2    | tm_mday(日)             | range [1, 31]             |
    | 3    | tm_hour(时)             | range [0, 23]             |
    | 4    | tm_min(分)              | range [0, 59]             |
    | 5    | tm_sec(秒)              | range [0, 61]             |
    | 6    | tm_wday(星期几)         | range [0, 6](0表示周日)   |
    | 7    | tm_yday(一年中的第几天)   | range [1, 366]            |
    | 8    | tm_isdst(是否是夏令时)   | 0, 1 or -1                |
    

    datetime模块

    datetime.datetime.now().strftime("%Y-%m-%d %X")

    主要是时间相关的封装,案例在time模块的下面:

    In [4]:

    import time
    
    # 时间戳,结果是float类型
    print(time.time())
    
    # 格式化的时间字符串:年-月-日 小时:分钟:秒
    print(time.strftime("%Y-%m-%d%X"))
    
    
    print(time.localtime())  # 本地时区的 struct_time
    print(time.gmtime())  # UTC时区(世界标准时间)的struct_time
    
    # time.mktime 将一个 struct_time 转化为时间戳
    print(time.mktime(time.localtime()))
    
    # t = time.localtime()
    # print(time.asctime(t)) # 把一个表示时间的元组或者struct_time表示为这种形式:'Sun Jun 20 23:21:05 1993'
    # print(time.asctime()) # 不写参数默认就是time.localtime()
    

     

    1530595914.981183
    2018-07-03 13:31:54
    time.struct_time(tm_year=2018, tm_mon=7, tm_mday=3, tm_hour=13, tm_min=31, tm_sec=54, tm_wday=1, tm_yday=184, tm_isdst=0)
    time.struct_time(tm_year=2018, tm_mon=7, tm_mday=3, tm_hour=5, tm_min=31, tm_sec=54, tm_wday=1, tm_yday=184, tm_isdst=0)
    1530595914.0
    

    In [5]:

    import datetime
    
    print(datetime.datetime.now())  # 当前时间 2018-07-03 13:02:20.824494
    print(datetime.datetime.now().strftime("%Y-%m-%d%X"))  # 当前时间 2018-07-03 13:02:20
    
    # 时间加减
    print(datetime.datetime.now()   datetime.timedelta(3))  # 当前时间 3天
    print(datetime.datetime.now()   datetime.timedelta(-3))  # 当前时间-3天
    print(datetime.datetime.now()   datetime.timedelta(hours=3))  # 当前时间 3小时
    print(datetime.datetime.now()   datetime.timedelta(minutes=30))  # 当前时间 30分
    

     

    2018-07-03 13:31:55.147268
    2018-07-03 13:31:55
    2018-07-06 13:31:55.147883
    2018-06-30 13:31:55.148109
    2018-07-03 16:31:55.148315
    2018-07-03 14:01:55.148532
    

     

    6.2.判断是否是迭代器¶

    迭代器是一定可以迭代的,怎么判断是迭代器呢?

    可以使用next方法的或者通过isinstance(xxx,Iterator)

    In [99]:

    a=[1,2,3]
    
    next(a)
    

     

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-99-f5f8ac9a8550> in <module>()
          1 a=[1,2,3]
          2 
    ----> 3next(a)
    
    TypeError: 'list' object is not an iterator
    

    In [100]:

    from collections import Iterator
    

    In [101]:

    isinstance([],Iterator)
    

    Out[101]:

    False
    

    In [102]:

    isinstance((x for x in range(10)),Iterator)
    

    Out[102]:

    True
    

     

    7.2.random¶

    random小明有好好研究,于是抢着讲解:

    常用方法主要是这几个:

    random.random():大于0且小于1之间的小数

    random.randint(1, 3):[1,3] 大于等于1且小于等于3之间的整数

    random.randrange(1, 3):[1,3) 大于等于1且小于3之间的整数

    random.choice([4, 'a', [1, 2]]):随机返回三者之一

    random.uniform(1, 3):大于1小于3的小数,如1.927109612082716

    random.sample(old_list, 5):从list中随机获取5个元素,作为一个片断返回

    random.shuffle(old_list):打乱list的顺序

    下面来看看例子吧(最下面有个简单验证码的demo):

    In [6]:

    import random
    

    In [7]:

    print(random.random())  # 大于0且小于1之间的小数
    
    print(random.randint(1, 3))  # [1,3]    大于等于1且小于等于3之间的整数
    
    print(random.randrange(1, 3))  # [1,3)    大于等于1且小于3之间的整数
    
    print(random.choice([4, 'a', [1, 2]]))  # 随机返回三者之一
    
    print(random.uniform(1, 3))  # 大于1小于3的小数,如1.927109612082716
    

     

    0.08269940155686328
    3
    2
    [1, 2]
    2.1607974783487136
    

    In [8]新葡亰496net,:

    # 从list中随机获取N个元素,作为一个片断返回
    old_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    new_list = random.sample(old_list, 5)  # 从list中随机获取5个元素,作为一个片断返回
    print(old_list)
    print(new_list)  # 原有序列并没有改变
    

     

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    [7, 6, 9, 10, 3]
    

    In [9]:

    test_list = [1, 3, 5, 7, 9]
    random.shuffle(test_list)  # 打乱test_list的顺序
    print(test_list)
    

     

    [1, 5, 7, 9, 3]
    

    In [10]:

    # 简单验证码的例子
    import random
    
    
    def get_code(n):
        """简单验证码"""
        code = ""
        for i in range(n):
            s1 = chr(random.randint(65, 90))  # 字母
            s2 = str(random.randint(0, 9))  # 数字
            code  = random.choice([s1, s2])  # 随机返回s1 or s2
        return code
    
    
    def main():
        print(get_code(4))
    
    
    if __name__ == '__main__':
        main()
    

     

    KOOH
    

     

    6.3.IterableIterator

    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator

    list、dict、strIterable变成Iterator可以使用iter()函数:

    In [103]:

    iter(a)
    

    Out[103]:

    <list_iterator at 0x7f14c40a3da0>
    

    In [104]:

    isinstance(iter([]),Iterator)
    

    Out[104]:

    True
    

    In [105]:

    isinstance(iter({}),Iterator)
    

    Out[105]:

    True
    

     

    Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()or__next__()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误

    可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

    Iterator甚至可以表示一个无限大的数据流,而list等则不行

    小明总结了一下老师讲解的知识点:

    1. 可以for循环的对象都是Iterable类型

    2. 可以使用next()or__next__()函数的对象都是Iterator类型

    3. 集合数据类型如list、dict、str等是Iterable,可以通过iter()函数获得一个Iterator对象

     

    7.3.hashlib¶

    一看到hashlib,小明就退缩了,只能继续听伟哥讲解:

    官方文档:

    简单hash:hashlib.sha256(pass_str.encode("utf-8")).hexdigest()

    和key一起加密:

    import hashlib
    
    pass_str = "123456"
    
    m = hashlib.sha256()
    
    m.update(pass_str.encode("utf-8"))
    
    m.update("你设置的key".encode("utf-8"))
    
    pass_str_new = m.hexdigest()
    

    扩展:python 还有一个hmac模块,它内部对我们创建keypass进行处理后再加密:

    In [11]:

    import hashlib
    
    pass_str = "123456"
    

    In [12]:

    # 简单hash一下
    m = hashlib.sha256()
    
    m.update(pass_str.encode("utf-8"))
    
    pass_str_new = m.hexdigest()
    
    print(pass_str_new)
    print(len(pass_str_new))
    

     

    8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
    64
    

    In [13]:

    # 简写:
    hashlib.sha256(pass_str.encode("utf-8")).hexdigest()
    

    Out[13]:

    '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
    

    In [14]:

    # 简写这么方便为什么要像上面例子那么复杂呢?
    # 直接加密的方式可以通过撞库破解,但你可以设置个key来加大难度
    # 大部分网站都是用平台key   用户key(可以是用户创建时间或者id之类的不变信息)来加密的
    m = hashlib.sha256()
    
    m.update(pass_str.encode("utf-8"))
    
    m.update("你设置的key".encode("utf-8"))
    
    pass_str_new = m.hexdigest()
    
    print(pass_str_new)
    print(len(pass_str_new))
    

     

    fadc86f44ab0f13346bf3293b5bc51112569a21e421809f93a096c917d7731ce
    64
    

     

    常用模块里面还有很多,就不一一介绍了,比如:functools里面的偏函数partial

    这个我们下次讲闭包相关知识会说的functools.partial(函数名, 默认参数1,默认参数2...)

    7.CSharp迭代器¶

    乘着下课的时间,小明跑到黑板前,心想:“又到了C#的时候了,看我来收播一大群眼球~”,然后开始了他的个人秀:

    其实迭代器(iterator就是为了更简单的创建枚举器(enumerator)和可枚举类型(enumerator type)的方式

    8.第三方模块安装¶

    第三方模块安装之前有说过:

    简单提一下:(pip官网)

    pip install 模块名 # 正常
    
    sudo pip install 模块名 # 没权限运行
    
    pip install --user 模块名 # conda下的pip
    

    貌似win下面有一个模块比较火virtualenv,作用好像可以使得pip安装的模块不会直接安装到系统中

    因为逆天是Ubuntu的,所以具体的情况我不太清楚,你可以作为扩展了解下(参考)


    7.1.IEnumeratorIEnumerable

    通俗话讲:

    能不能foreach就看你遍历对象有没有实现IEnumerable,就说明你是不是一个可枚举类型enumerator type

    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }
    

    是不是个枚举器(enumerator)就看你实现了IEnumerator接口没

    public interface IEnumerator
    {
        object Current { get; }
    
        bool MoveNext();
    
        void Reset();
    }
    

    最明显的区别:它们两个遍历方式不一样

    // 枚举器遍历
    var tmp = FibonaByIEnumerator(30);
    while (tmp.MoveNext())
    {
        Console.WriteLine(tmp.Current);
    }
    // 可枚举类型遍历
    foreach (var item in FibonaByIEnumerable(30))
    {
        Console.WriteLine(item);
    }
    

    这个我们在2年前就说过,这边简单提一下(官方文档)(Demo)

    MyEnumerator文件:

    public class MyEnumerator : IEnumerator
    {
        /// <summary>
        /// 需要遍历的数组
        /// </summary>
        private string[] array;
        /// <summary>
        /// 有效数的个数
        /// </summary>
        private int count;
        public MyEnumerator(string[] array, int count)
        {
            this.array = array;
            this.count = count;
        }
    
        /// <summary>
        /// 当前索引(线moveNext再获取index,用-1更妥)
        /// </summary>
        private int index = -1;
        public object Current
        {
            get
            {
                return array[index];
            }
        }
        /// <summary>
        /// 移位
        /// </summary>
        /// <returns></returns>
        public bool MoveNext()
        {
            if (  index < count)
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 重置
        /// </summary>
        public void Reset()
        {
            index = -1;
        }
    }
    

    MyArray.cs文件

    public partial class MyArray
    {
        /// <summary>
        /// 数组容量
        /// </summary>
        private string[] array = new string[4];
        /// <summary>
        /// 数组元素个数
        /// </summary>
        private int count = 0;
        /// <summary>
        /// 当前数组的长度
        /// </summary>
        public int Length
        {
            get
            {
                return count;
            }
        }
    
        /// <summary>
        /// 添加元素
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public MyArray Add(string str)
        {
            //要溢出的时候扩容
            if (count == array.Length)
            {
                string[] newArray = new string[2 * array.Length];
                array.CopyTo(newArray, 0);
                array = newArray;//array重新指向
            }
            array[count  ] = str;
            return this;
        }
    
        /// <summary>
        /// 移除某一项
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public MyArray RemoveAt(int i)
        {
            for (int j = i; j < count - 1; j  )
            {
                array[j] = array[j   1];
            }
            count--;//少了一个元素所以--
            return this;
        }
    
        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public string this[int index]
        {
            get
            {
                return array[index];
            }
            set
            {
                array[index] = value;
            }
        }
    }
    

    MyArrayExt.cs文件:

    public partial class MyArray: IEnumerable
    {
        /// <summary>
        /// 枚举器方法
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return new MyEnumerator(this.array, this.count);
        }
    }
    

    调用:

    static void Main(string[] args)
    {
        MyArray array = new MyArray();
        array.Add("~").Add("这").Add("是").Add("一").Add("个").Add("测").Add("试").Add("。").RemoveAt(0).RemoveAt(3).RemoveAt(6);
        for (int i = 0; i < array.Length; i  )
        {
            Console.Write(array[i]);
        }
        Console.WriteLine();
        foreach (var item in array)
        {
            Console.Write(item);
        }
    }
    

    结果:

    这是一测试
    这是一测试
    

     

    9.包系列(导入、发布、安装)¶

    如果不同的人编写的模块名相同怎么办?

    小明抢答曰:”为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)“

    小张斜了一眼小明补充道:”相互间有联系功能的模块可以放在一个包里,这个包就有点像C#的文件夹和类库的概念了“

    小明不服气的把小潘那段代码的开头贴了出来:import matplotlib.pyplot as plt,然后叫嚣道:”怎么样,这个肯定用到了包“

    7.2.yield方式¶

    小明看着班里女生羡慕的眼神,得意的强调道:

    注意一下,C#是用yield return xxx,Python是用yield xxx关键字

    还记得开头说的那句话吗?(yield官方文档)

    其实迭代器(iterator)就是为了更简单的创建枚举器(enumerator)和可枚举类型(enumerator type)的方式

    如果枚举器和可枚举类型还是不理解(举个例子)就懂了:(从遍历方式就看出区别了)

    定义一个斐波拉契函数,返回可枚举类型

    /// <summary>
    /// 返回一个可枚举类型
    /// </summary>
    public static IEnumerable<int> FibonaByIEnumerable(int n)
    {
        int a = 0;
        int b = 1;
        for (int i = 0; i < n; i  )
        {
            yield return b;
            (a, b) = (b, a   b);
        }
    }
    

    调用:

    foreach (var item in FibonaByIEnumerable(30))
    {
        Console.WriteLine(item);
    }
    

    定义一个斐波拉契函数,返回一个枚举器

    /// <summary>
    /// 返回一个枚举器
    /// </summary>
    public static IEnumerator<int> FibonaByIEnumerator(int n)
    {
        int a = 0;
        int b = 1;
        for (int i = 0; i < n; i  )
        {
            yield return b;
            (a, b) = (b, a   b);
        }
    }
    

    调用一下:

    var tmp = FibonaByIEnumerator(30);
    while (tmp.MoveNext())
    {
        Console.WriteLine(tmp.Current);
    }
    

    利用yield轻轻松松就创建了枚举器和可枚举类型

    以上面那个MyArray的案例来说,有了yield我们代码量大大简化:(Demo)

    MyArray.cs

    public partial class MyArray
    {
        /// <summary>
        /// 数组容量
        /// </summary>
        private string[] array = new string[4];
        /// <summary>
        /// 数组元素个数
        /// </summary>
        private int count = 0;
        /// <summary>
        /// 当前数组的长度
        /// </summary>
        public int Length
        {
            get
            {
                return count;
            }
        }
    
        /// <summary>
        /// 添加元素
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public MyArray Add(string str)
        {
            //要溢出的时候扩容
            if (count == array.Length)
            {
                string[] newArray = new string[2 * array.Length];
                array.CopyTo(newArray, 0);
                array = newArray;//array重新指向
            }
            array[count  ] = str;
            return this;
        }
    
        /// <summary>
        /// 移除某一项
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public MyArray RemoveAt(int i)
        {
            for (int j = i; j < count - 1; j  )
            {
                array[j] = array[j   1];
            }
            array[count - 1] = string.Empty;//add 干掉移除的数组
            count--;//少了一个元素所以--
            return this;
        }
    
        /// <summary>
        /// 索引器
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public string this[int index]
        {
            get
            {
                return array[index];
            }
            set
            {
                array[index] = value;
            }
        }
    }
    

    MyArrayExt.cs

    public partial class MyArray : IEnumerable
    {
        /// <summary>
        /// 枚举器方法
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return MyEnumerator();
        }
        /// <summary>
        /// 通过yield快速实现
        /// </summary>
        /// <returns></returns>
        public IEnumerator<string> MyEnumerator()
        {
            foreach (var item in this.array)
            {
                yield return item;
            }
        }
    }
    

    然后就行了,MyEnumerator都不用你实现了:

    MyArray array = new MyArray();
    array.Add("~").Add("这").Add("是").Add("一").Add("个").Add("测").Add("试").Add("。").RemoveAt(0).RemoveAt(3).RemoveAt(6);
    for (int i = 0; i < array.Length; i  )
    {
        Console.Write(array[i]);
    }
    Console.WriteLine();
    foreach (var item in array)
    {
        Console.Write(item);
    }
    

    结果:

    这是一测试
    这是一测试
    

    扩充一下:Python退出迭代器用yield return 或者 yield breakC#使用yield break来退出迭代

    做个 demo 测试下:

    public static IEnumerable<int> GetValue()
    {
        for (int i = 0; i < 5; i  )
        {
            yield return i;
            if (i == 2)
            {
                yield break;
            }
        }
    }
    

    调用:

    static void Main(string[] args)
    {
        foreach (var item in GetValue())
        {
            Console.WriteLine(item);
        }
    }
    

    输出:

    0
    1
    2
    

     

    9.1.包的定义¶

    课代表看着这两个活宝,然后继续讲解道:

    模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用

    比如一个模块的名称是A.B, 那么他表示一个包A中的子模块B

    刚说完,小明就得意的看着小张,然后继续听课:

    创建自己的模块时,要注意:

    1. 模块名要遵循Python变量命名规范,不要使用中文、特殊字符
    2. 模块名不要和系统模块名冲突(标准库)

    下面我们来一个简单的案例:

    自己定义一个包,目录结构如下:(__init__.py是空文件)

    web/
    ├── data.py
    ├── __init__.py
    └── json.py
    

    调用web包里面的get_json()方法:

    from web.json import get_json
    
    get_json()
    

    或者:

    import web.json as json
    
    json.get_json()
    

    每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则Python就把这个目录当成普通目录

    __init__.py可以是空文件,编译器默认会把它认成名字是文件夹名的模块,eg:web

    8.闭包¶

    9.2.包的制定化 ~ 控制可使用的模块¶

    还记得之前讲得__all__吧,如果你想控制包里面的模块能否被访问使用,可以使用它

    比如web包下的init文件中写下这一句:__all__ = ["json"]

    神奇的事情发生了:

    from web import *
    
    json.get_json()
    
    data.get_data()
    

    在调用data.get_data()的时候出现了如下错误:

    NameError: name 'data' is not defined
    

    小明想到了自己当时的那顿饭,然后用当时的思路试了下:

    # 你这样调就不行了
    import web
    
    web.data.get_data()
    

    发现和上面一样的错误,然后夸张的说道~“哈哈哈,这种方式无敌了,里面有谁能调data模块下的get_data()方法我就请他喝饮料!”

    话刚落完,发现大家刷刷的提交了相同的代码:

    # 同样的,你硬是要调,python也拿你没办法
    import web.data as data
    
    data.get_data()
    

    小明顿时崩溃了,自我安慰到:“反正都是要请你们吃晚饭的了,也不差这饮料了...”

     

    8.1.Python闭包¶

    又到了上课时间,小明灰溜溜的跑回座位,听老师讲起了闭包的知识:

    函数方面还有不懂的可以看之前讲的文档:Function Base

    函数除了可以接受函数作为参数外,还可以把函数作为结果值返回(有点类似于C 里面的函数指针了)

    来看一个可变参数求和的例子:

    In [1]:

    def slow_sum(*args):
        def get_sum():
            sum = 0
            for i in args:
                sum  = i
            return sum
    
        return get_sum  # 返回函数引用地址(不加括号)
    
    a = slow_sum(1, 2, 3, 4, 5)# 返回get_sum函数的引用
    print(a)# 看看引用地址
    print(a())# a() 这时候才是调用get_sum()函数
    

     

    <function slow_sum.<locals>.get_sum at 0x7f57783b6268>
    15
    

     

    其实上面一个案例就是闭包(Closure)了,来个定义:

    在函数内部再定义一个函数,并且这个函数用到了外边函数的变量(参数或者局部变量),那么将这个函数以及用到的一些变量称之为闭包

    通俗点说就是:内部函数使用了外部函数作用域里的变量了,那这个内部函数和它用到的变量就是个闭包

    注意:当我们调用slow_sum()时,每次调用都会返回一个新的函数(相同的参数也一样)

    In [2]:

    a = slow_sum(1, 2, 3, 4)
    b = slow_sum(1, 2, 3, 4)
    
    a is b
    
    # a()和b()的调用结果互不影响
    

    Out[2]:

    False
    

     

    由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,所以也容易消耗内存

    so ==> 除非你真正需要它,否则不要使用闭包

    返回函数尽量不要引用任何循环变量,或者后续会发生变化的变量(容易出错)

    看着小明一脸懵圈的样子,老师说道:

    新讲的知识点一般都不太容易快速消化,我们再来看个闭包的好处就理解了:

    比如现在我们要根据公式来求解,以y=ax b为例,传统方法解决:

    In [3]:

    # 定义一个y=ax b的函数公式
    def get_value(a, b, x):
        return a * x   b
    

    In [4]:

    # 每次调用都得传 a,b
    print(get_value(2, 1, 1))
    print(get_value(2, 1, 2))
    print(get_value(2, 1, 3))
    print(get_value(2, 1, 4))
    

     

    3
    5
    7
    9
    

     

    每次调用都得额外传a、b的值

    就算使用偏函数来简化也不合适(毕竟已经是一个新的函数了):

    In [5]:

    from functools import partial
    
    new_get_value = partial(get_value, 2, 1)
    
    print(new_get_value(1))
    print(new_get_value(2))
    print(new_get_value(3))
    print(new_get_value(4))
    print(new_get_value(5))
    

     

    3
    5
    7
    9
    11
    

     

    简单总结functools.partial的作用就是:

    把一个函数的某些参数设置默认值,返回一个新的函数,然后调用新函数就免得你再输入重复参数了

    而这时候使用闭包就比较合适了,而且真的是封装了一个通用公式了

    a,b的值你可以任意变来生成新的公式,而且公式之间还不干扰,以 y=ax² bx c为例:

    In [6]:

    def quadratic_func(a, b, c):
        """y=ax² bx c"""
    
        def get_value(x):
            return a * x * x   b * x   c
    
        return get_value
    

    In [7]:

    # 来个简单的:x^2 1
    f1 = quadratic_func(1, 0, 1)
    
    print(f1(0))
    print(f1(1))
    print(f1(2))
    print(f1(3))
    print(f1(4))
    print(f1(5))
    

     

    1
    2
    5
    10
    17
    26
    

    In [8]:

    # 可能不太形象,我们画个图看看:
    
    import matplotlib.pyplot as plt # 导入matplotlib的pyplot模块
    

    In [9]:

    # 生成x和y的值
    x_list = list(range(-10, 11))
    y_list = [x * x   1 for x in x_list]
    
    print(x_list)
    print(y_list)
    
    # 画图
    plt.plot(x_list, y_list)
    # 显示图片
    plt.show()
    

     

    [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    [101, 82, 65, 50, 37, 26, 17, 10, 5, 2, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 101]
    

     

    新葡亰496net 10

    In [10]:

    # 再来个简单的:x^2-1
    f2 = quadratic_func(1, 0, -1) # 相互之间不干扰
    
    print(f2(0))
    print(f2(1))
    print(f2(2))
    print(f2(3))
    print(f2(4))
    print(f2(5))
    

     

    -1
    0
    3
    8
    15
    24
    

     

    9.3.发布一个包¶

    把我们之前的Code做一个包,目录结构如下:

    ├── datalib
    │   ├── base
    │   │   ├── get_user_infos.py
    │   │   ├── __init__.py
    │   │   ├── test1.py
    │   │   ├── test2.py
    │   │   ├── test3.py
    │   │   ├── test4.py
    │   │   └── test.py
    │   ├── __init__.py
    │   ├── main.py
    │   └── web
    │       ├── data.py
    │       ├── __init__.py
    │       └── json.py
    └── setup.py
    

    模块的目录结构中包含一个setup.py文件,用于定义模块名称、包含的模块等模块信息

    固定格式如下:

    setup(name="模块名称", version="版本", description="模块描述", author="模块作者", py_modules=[包含的模块内容])
    

    以demo为例:

    from distutils.core import setup
    
    setup(
        name="datalib",
        version="1.0",
        description="数据相关的模块",
        author="小明",
        py_modules=[
            "datalib.base.get_user_infos", "datalib.base.test", "datalib.web.json"
        ])
    

    build命令(构建模块):python setup.py sdist 来张图更形象:

    新葡亰496net 11

    build文件夹:

    dnt@MZY-PC:~/xiaoming/build$ tree
    .
    └── lib
        └── datalib
            ├── base
            │   ├── get_user_infos.py
            │   ├── __init__.py
            │   └── test.py
            └── web
                ├── __init__.py
                └── json.py
    

    生成发布压缩包:python3 setup.py sdist 他会帮你打包成一个tar.gz的压缩包

    其实你不构建,直接sdist也是可以的

    dnt@MZY-PC:~/xiaoming$ tree
    .
    ├── datalib
    │   ├── base
    │   │   ├── get_user_infos.py
    │   │   ├── __init__.py
    │   │   ├── test1.py
    │   │   ├── test2.py
    │   │   ├── test3.py
    │   │   ├── test4.py
    │   │   └── test.py
    │   ├── __init__.py
    │   ├── main.py
    │   └── web
    │       ├── data.py
    │       ├── __init__.py
    │       └── json.py
    ├── dist
    │   └── datalib-1.0.tar.gz
    ├── MANIFEST
    └── setup.py
    

    8.2.CSharp闭包¶

    听完闭包老师就下课了,说什么明天接着闭包讲啥装饰器的。

    小明一愣一愣的,然后就屁颠的跑黑板前讲起了C#版本的闭包:

    先看看怎么定义一个闭包,和Python一样,用个求和函数举例:(返回一个匿名函数

    // 有返回值就用Func,没有就用Action
    public static Func<int> SlowSum(params int[] args)
    {
        return () =>
        {
            int sum = 0;
            foreach (var item in args)
            {
                sum  = item;
            }
            return sum;
        };
    }
    

    调用:

    static void Main(string[] args)
    {
        var f1 = SlowSum(1, 2, 3, 4, 5);
        Console.WriteLine(f1);
        Console.WriteLine(f1());
    }
    

    结果:(从结果可以看到,f1是一个函数,等你调用f1()才会求和)

    System.Func`1[System.Int32]
    15
    

    接着讲 ~ 以上面的 y=ax² bx c为例,C#实现:

    // 以上面的 y=ax² bx c 为例,C#实现:
    public static Func<double, double> QuadraticFunc(double a, double b, double c)
    {
        return x => a * x * x   b * x   c; // 返回一个匿名函数
    }
    

    调用:

    static void Main(string[] args)
    {
        var func = QuadraticFunc(1, 0, 1);
    
        Console.WriteLine(func(0));
        Console.WriteLine(func(1));
        Console.WriteLine(func(2));
        Console.WriteLine(func(3));
        Console.WriteLine(func(4));
        Console.WriteLine(func(5));
    }
    

    结果:

    1
    2
    5
    10
    17
    26
    

    Func<double,double>不理解就看看定义就懂了:public delegate TResult Func<in T, out TResult>(T arg);

    这部分不是很难,简单提一下知识点即可。如果你想深究可以==> (点 点 点)

    在收获满满一箩筐眼球后,小明拍拍屁股去了新开的饭店大吃一顿了...


    写在最后:还有一些内容没写,估计过几天又有一篇叫 “基础拓展” 的文章了,为啥不一起写完呢?

    其实逆天也想写完,真写完文章又被叫做长篇大论一百页了 #^_^# 行了,听取大家意见,不写那么长的文章,下次见~

    9.4.安装自己打包的模块¶

    安装比较简单,如果是build文件夹,直接sudo python setup.py install

    如果是tar.gz,那么解压后再执行sudo python setup.py install

    当然了,也可以目录安装:python setup.py install --prefix=路径

    查看包内容:tar -tvf datalib-1.0.tar.gz

    解包:tar -zxvf datalib-1.0.tar.gz

    这些基础命令如果还不是很熟悉的话,可以看看逆天2015年写的LinuxBase文档

    其实说句心里话,2015开始摸索虚拟机中的Linux,几年提升肯定是有的,但是绝对没有这几个月的Linux真机开发来得快

    逆天还是建议程序员尽快适应Linux环境开发,刚开始的确不方便,适应之后就感觉Win特别麻烦了...

    很多东西一个指令就可以实现的,重复执行的东西写个脚本批量执行

    当然了你Win下各种跑脚本也是一样的,但是别忘了~公司服务器基本上都是Linux的,该面对的总该来的,加油~

     

    CSharp包相关知识¶

    Old C#相关的知识可以参考:如何在nuget上传自己的包

    NetCore可以用打包命令:dotnet-pack来打包成Nuget包,官方文档说的很详细了,就不一一复述了

    打包当前目录中的项目:dotnet pack

    打包 app1 项目:dotnet pack ~/projects/app1/project.csproj

    打包当前目录中的项目并将生成的包放置到 nupkgs 文件夹:

    dotnet pack --output nupkgs

    将当前目录中的项目打包到 nupkgs 文件夹并跳过生成步骤:

    dotnet pack --no-build --output nupkgs

    使用 PackageVersion MSBuild 属性将包版本设置为 2.1.0:

    dotnet pack /p:PackageVersion=2.1.0

    打包特定目标框架的项目:

    dotnet pack /p:TargetFrameworks=net45

    打包项目,并使用特定运行时 (Windows 10) 进行还原操作(.NET Core SDK 2.0 及更高版本):

    dotnet pack --runtime win10-x64

     

    小明听完了这节课,感叹道~ “模块用起来真是爽啊”

    然后问道:“为什么老师没讲呢?”

    其他人一起鄙视小明说道:“你自己睡大觉你自己心里没有点逼数吗?”

    小明大囧,感觉饮料和饭钱真是白花了。。。(完)

    本文由新葡亰496net发布于奥门新萄京娱乐场,转载请注明出处:新葡亰496net:继承与多态,面向对象之

    关键词:

上一篇:新葡亰496net文档翻译,基本操作符

下一篇:没有了