Py003-01-08封装的意义

封装的意义

  • 封装数据属性:明确的区分内外,控制外部对隐藏属性的操作行为

将数据隐藏起来,外部无法直接访问,然后暴露接口访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class People:
def __init__(self,name,age):
self.__name = name
self.__age = age

def tellInfo(self):
print("我是%s,今年%s岁"%(self.__name,self.__age))
def setInfo(self,name,age):
if not isinstance(name,str):
print('名字必须是字符串')
return
if not isinstance(age, int):
print('age必须是数字')
return
self.__name = name
self.__age = age


p = People('hjx',18)
p.tellInfo()
p.setInfo('hjx2','aaa') # 设置年龄失败
  • 封装方法:隔离复杂度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取出金额')
def __print_bill(self):
print('打印出账单')
def __take_money(self):
print('取款')

def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()

支付宝,微信支付,qq支付,

Py003-01-07封装

封装

属性和方法的隐藏,外部不能直接通过属性和方法名访问

  • 双下划线前缀的属性和方法,在类外部无法直接这样访问
  • 但在类的内部可以访问这些“双下划线前缀的属性和方法”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

class A:
__x = 1

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

def __foo(self):
print('run foo')

def bar(self):
self.__foo()
print('from bar')

# print(A.__x) 报错
# print(A.__foo()) 报错
# print(A.bar()) 不报错

# 即使创建该类对象 也无法访问双下划线的属性和方法
a = A('hjx')
# print(a.__name) 报错
# print(a.__foo()) 报错

# 可以通过 __dict__ 访问A类的相关属性信息
print(A.__dict__)
'''
{
'__module__': '__main__',
'_A__x': 1,
'__init__': <function A.__init__ at 0x00000187A059AF28>,
'_A__foo': <function A.__foo at 0x00000187A059AEA0>,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': None
}
'''

我们发现 dict信息里属性通过一种方式——变形

1
2
3
4
5
6
7
8
9
class A:
__x = 1 # _A__x = 1
def __init__(self,name):
self.__name = name # _A__name = name
def __foo(self): # def _A__foo(self)
print('run foo')

类中的 __x 会变为 _A__x
类中的 __foo 会变为 _A__foo

这种变形的特点

  • 在类外部无法直接obj.__属性名
  • 类内部可以通过:obj.__属性名
  • 子类无法覆盖父类双下划线前缀的属性和方法
    (因为在类执行前 双下划线前缀的属性和方
    法会产生变形)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class A:
    __x = 1 # 变形为 _A__x
    def __foo(self): # 变形为 def _A__foo(self):
    print('from foo A')

    class B(A):
    __x = 2 # 变形为 _B__x
    def __foo(self): # 变形为 def _B__foo(self):
    print('from foo B')

知道变形规则后你可以这样投机的访问这些双下划线前缀的属性和方法

1
2
3
4
# 这样就不报错了
a = A('hjx')
print(a._A__x)
a._A__foo()

这种变形需要注意的问题:

  • 如果想隐藏属性和方法你就添加双下划线前缀,你知道了变形规则和访问方式还想直接访问那索性不如不加双下划线前缀
1
2
3
4
5
6
class B:
__x = 1
y = 2

print(B._B__x) # 你都隐藏了还这样访问? 是不是傻?
print(B.y) # 既然想访问就别费劲了 还加双下划线前缀
  • 双下划线前缀的属性和方法只在类定义的时候变形,后续添加的双下划线前缀属性或方法不会进行——变形
1
2
3
4
5
6
7
class B:
__x = 1

B.__y = 2
print(B.__dict__)
print(B.__y) # 像普通属性一样访问
# 自己看吧!

所以说你要隐藏属性或方法,要在类定义的时候进行

  • 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

先看我们之前的学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A:
def foo(self):
print('from a foo')
def bar(self):
print('from a bar')
self.foo()

class B(A):
def foo(self):
print('from b foo')

b = B()
b.bar()
'''
执行结果:
from a bar
from b foo
1. b本身没有bar方法
2. 于是从父类A查找 bar方法 找到了
3. 执行 bar的时候 调用 self.foo() 此时的self是 b对象 所以 self.foo() 相当于 b.foo()
'''

实现我们的需求 子类调用父类的同名方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A:
def __foo(self): # _A__foo
print('from a foo')
def bar(self):
print('from a bar')
self.__foo() # self._A_foo

class B(A):
def __foo(self):
print('from b foo')

b = B()
b.bar()

'''
执行结果
from a bar
from a foo
'''

Py003-01-06多态与多态性

多态

同类事物有多种形态

  • 动物的几种形态,人狗猪三种形态
  • 游戏里角色的几种形态,法师战士弓箭手

多态性

在不考虑实例类型的情况下,直接使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import abc
class Animal(metaclass=abc.ABCMeta):
category = 'animal'
@abc.abstractmethod
def run(self):
pass

class People(Animal):
def run(self):
print('人 跑')

class Pig(Animal):
def run(self):
print('猪 跑')

class Dog(Animal):
def run(self):
print('狗 跑')


# 多态性:在不考虑实例类型的情况下,直接使用实例
people = People()
dog = Dog()
pig = Pig()

# 它们都能 调用对应的 run方法
'''
people.run()
dog.run()
pig.run()
'''

# 统一的形式去调用,增加程序的灵活性
def commonRun(animal):
animal.run()

commonRun(people)
commonRun(pig)
commonRun(dog)

为啥用多态性

1.增加了程序的灵活性

  以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如commonRun(animal)

2.增加了程序额可扩展性

 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用commonRun(animal)去调用   

1
2
3
class Cat(Animal):
def run(self):
print('猫 跑')

鸭子类型

Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象

也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

你所学的序列类型

  • 列表list
  • 元组tuple
  • 字符串str
1
2
3
4
5
6
7
8
9
10
11
12
13
l = list([1,2,3])
t = tuple(('a','b'))
s = str('hello')

# 此时都有len方法 你需要考虑它是 list tuple str吗?
print(l.__len__())
print(t.__len__())
print(s.__len__())

'''
def len(obj):
return obj.__len__()
'''

所以说你看着像鸭子,没准你就真的是鸭子

学习也是一样

fake it until you make it

如果你不是一个优秀的人,你就假装你是一个优秀的人(模仿优秀的人做的事),久而久之你也变成了一个优秀的人。

Py003-01-05抽象类与归一化

抽象类与归一化

以下都属于动物,它们都有走的方法,只是形式不同

java中提供一种接口的方式来实现方法的统一,但是python中没有,以下为java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
=================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java
/*
* Java的Interface接口的特征:
* 1)是一组功能的集合,而不是一个功能
* 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作
* 3)接口只定义函数,但不涉及函数实现
* 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */

package com.oo.demo;
public interface IAnimal {
public void eat();
public void run();
public void sleep();
public void speak();
}

=================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口
package com.oo.demo;
public class Pig implements IAnimal{ //如下每个函数都需要详细实现
public void eat(){
System.out.println("Pig like to eat grass");
}

public void run(){
System.out.println("Pig run: front legs, back legs");
}

public void sleep(){
System.out.println("Pig sleep 16 hours every day");
}

public void speak(){
System.out.println("Pig can not speak"); }
}

=================第三部分:Person2.java
/*
*实现了IAnimal的“人”,有几点说明一下:
* 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样
* 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */

package com.oo.demo;
public class Person2 implements IAnimal {
public void eat(){
System.out.println("Person like to eat meat");
}

public void run(){
System.out.println("Person run: left leg, right leg");
}

public void sleep(){
System.out.println("Person sleep 8 hours every dat");
}

public void speak(){
System.out.println("Hellow world, I am a person");
}
}

=================第四部分:Tester03.java
package com.oo.demo;

public class Tester03 {
public static void main(String[] args) {
System.out.println("===This is a person===");
IAnimal person = new Person2();
person.eat();
person.run();
person.sleep();
person.speak();

System.out.println("\n===This is a pig===");
IAnimal pig = new Pig();
pig.eat();
pig.run();
pig.sleep();
pig.speak();
}
}

java中的interface

如果你非要实现如上的接口一样的效果,你需要借助一个模块 abc模块

抽象类概念

  • 抽象类只能被继承,无法被实例化
  • 抽象类定义的抽象方法子类必须实现否则报错
  • 抽象类的属性子类可以查找到(前提是子类本身没有该属性)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import abc

# 只进行接口的定义
class Animal(metaclass=abc.ABCMeta):
category = 'animal'
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def eat(self):
pass

class People(Animal):
def run(self):
print('人 跑')
def eat(self):
print('人 吃饭')

class Pig(Animal):
def run(self):
print('猪 跑')
def eat(self):
print('猪 吃饭')

class Dog(Animal):
def run(self):
print('狗 跑')
def eat(self):
print('狗 吃饭')

# a = Animal() 报错 抽象类 只能被继承,无法实例化

people = People()
people.run()
people.eat()

pig = Pig()
pig.run()
pig.eat()

dog = Dog()
dog.run()
dog.eat()

我们知道People继承了 Animal,如果注释了People内部的eat方法就会报错。因为抽象方法必须实现

这样就实现了接口的统一,归一化

Py003-01-04组合

继承是类与类之间的关系

组合

先看这样一段代码 老师和学生

  • 有共用的属性 school
  • 有重复的属性 name,age,sex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Teacher:
school = '中科院'

def __init__(self,name,age,sex,level,salary):
self.name = name
self.age = age
self.sex = sex

self.level = level
self.salary = salary

def teach(self):
print('%s is teaching'%self.name)


class Student:
school = '中科院'
def __init__(self,name,age,sex,classname):
self.name = name
self.age = age
self.sex = sex

self.classname = classname

def learn(self):
print('%s is learning'%self.name)

提取公共

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class People:
school = '中科院'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


class Teacher(People):
def __init__(self,name,age,sex,level,salary):
super().__init__(name,age,sex)
self.level = level
self.salary = salary

def teach(self):
print('%s is teaching'%self.name)


class Student(People):
def __init__(self,name,age,sex,classname):
super().__init__(name, age, sex)
self.classname = classname

def learn(self):
print('%s is learning'%self.name)

老师有课程,如果都教 python呢?

1
2
3
4
5
6
7
8
9
10
11
12
class Teacher(People):
def __init__(self,name,age,sex,level,salary,course_name,course_price,course_period):
super().__init__(name,age,sex)
self.level = level
self.salary = salary
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period

# 这样就又重复了 俩个老师都教 python
t1 = Teacher('阮老师',49,'man',10,3000,'python',30000,'半年')
t2 = Teacher('廖雪峰',59,'man',10,3000,'python',30000,'半年')

继续提取课程 类

1
2
3
4
5
6
7
8
9
class Course:
def __init__(self,course_name,course_price,course_period):
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period

python = Course('python',30000,'半年')

此时老师和课程的关系?
  • 继承的关系是?——什么是什么
  • 组合呢?——什么有什么 如:老师有课程 学生有课程
1
2
3
4
5
6
7
8
9
10
11
t1 = Teacher('阮老师',49,'man',10,3000)
t2 = Teacher('廖雪峰',59,'man',10,3000)

t1.course = python
t2.course = python

# 访问t1/t2老师的课程名称
t1.course.course_name
t2.course.course_name

这种方式不是继承而是组合

Py003-01-03子类使用父类属性和方法

子类使用父类属性和方法

指名道姓方式(不依赖继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

class Hero:
def __init__(self, nickname, life_value, aggresivity):
self.nickname = nickname
self.life_value = life_value
self.aggresivity = aggresivity

def attack(self, enemy):
enemy.life_value = self.aggresivity


class AX(Hero):
camp = '斧王'

def attack(self, enemy):
Hero.attack(self,enemy)
print('from Garen Class')

ax = AX('axe',100,50)
enemy = Hero('xxx',100,40)
ax.attack(enemy) # 50

子类想要个武器

很重复的写法

1
2
3
4
5
6
7
8
9
10
11
12
class AX(Hero):
camp = '斧王'
def __init__(self, nickname, life_value, aggresivity,weapon):
self.nickname = nickname
self.life_value = life_value
self.aggresivity = aggresivity
self.weapon = weapon
def attack(self, enemy):
Hero.attack(self,enemy)
print('from Garen Class')

ax = AX('axe',100,50,'金箍棒')

继续指名道姓

1
2
3
4
5
6
7
8
9
10
11
12
# 子类有武器 指名道姓
class AX(Hero):
camp = '斧王'
def __init__(self, nickname, life_value, aggresivity,weapon):
Hero.__init__(self, nickname, life_value, aggresivity)
self.weapon = weapon
def attack(self, enemy):
Hero.attack(self,enemy)
print('from Garen Class')

ax = AX('axe',100,50,'金箍棒')
print(ax.__dict__)

在子类派生出的新的方法中重用父类的方法,有两种方式

  • 指名道姓(不依赖继承)
  • super() (依赖继承)

调用方法

1
2
3
4
5
6
class AX(Hero):
camp = '斧王'
def attack(self, enemy):
# Hero.attack(self,enemy)
super(AX,self).attack(enemy)
print('from Garen Class')

使用父类的初始化方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# super
class AX(Hero):
camp = '斧王'
def __init__(self, nickname, life_value, aggresivity,weapon):
# 指名道姓
# Hero.__init__(self, nickname, life_value, aggresivity)
# python2中
super(AX,self).__init__(nickname, life_value, aggresivity)
# python3
super().__init__(nickname, life_value, aggresivity)
self.weapon = weapon
def attack(self, enemy):
Hero.attack(self,enemy)
print('from Garen Class')

ax = AX('axe',100,50,'金箍棒')
# print(ax.__dict__)

依赖继承的一道恶心的题

1
2
3
4
5
6
7
8
9
10
11
12
class A:
def f1(self):
print('from a')

class B:
def f1(self):
print('from b')

class C(A,B):
pass

print(C.mro()) # C A B Object

再看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A:
def f1(self):
print('from a')
super().f1()

class B:
def f1(self):
print('from b')

class C(A,B):
pass

c = C()
c.f1()

结果:
from a
from b

C是依赖于继承的 所以会参照 C的mro列表查找 属性和方法

Py003-01-02继承

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Parent1:
pass
class Parent2:
pass

class Sub1(Parent1): # 单继承
pass

class Sub2(Parent1,Parent2): # 多继承
pass

# 查看继承关系
print(Sub1.__bases__)
print(Sub2.__bases__)
'''
(<class '__main__.Parent1'>,)
(<class '__main__.Parent1'>, <class '__main__.Parent2'>)
'''

再看英雄类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

class Hero:
def __init__(self,name,life_value,aggresivity):
self.name = name
self.life_value = life_value
self.aggresivity = aggresivity

def attack(self,enemy):
enemy.life_value -= self.aggresivity

class AX(Hero):
pass


ax = AX('斧王',30,40)
print(ax.__dict__) # {'name': '斧王', 'life_value': 30, 'aggresivity': 40}

属性查找顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Hero:
x = 1
y = 3
def __init__(self,name,life_value,aggresivity):
self.name = name
self.life_value = life_value
self.aggresivity = aggresivity

def attack(self,enemy):
enemy.life_value -= self.aggresivity

class AX(Hero):
pass

class Sevn(Hexo):
y = 4
pass

ax = AX('斧王',30,40)
ax.x # 1 自己本身没有去父类查找 如果父类没有报错

sevn = Sevn('斯温',50,60)

sevn.y # 4 子类有就不去父级查找

高深的属性查找,看你晕不晕

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Foo:
def f1(self):
print('from Foo.f1')
def f2(self):
print('from Foo.f2')

class Bar(Foo):
def f2(self):
print('from Bar.f2')

b = Bar()
b.f2() # from Bar.f2

------------------------------------------------------------------------
再看
------------------------------------------------------------------------

class Foo:
def f1(self):
print('from Foo.f1')
def f2(self):
print('from Foo.f2')
self.f1()

class Bar(Foo):
def f1(self):
print('from Bar.f1')

b = Bar()
b.f2()
'''
from Foo.f2
from Bar.f1 为啥是这个?不是就近吗? 要看self是谁 self一直是b
'''

派生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Hero:
def __init__(self,name,life_value,aggresivity):
self.name = name
self.life_value = life_value
self.aggresivity = aggresivity

def attack(self,enemy):
enemy.life_value -= self.aggresivity

class AX(Hero):
camp = '斧子'
def attack(self,enemy):
print('反刺螺旋')
pass

class Sevn(Hexo):
camp = '真男人'
def attack(self,enemy):
print('锤子')
pass

继承实现原理

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类
1
2
class Foo(a1,a2,a3,a4....an):
pass
  • 仅在python2中有 新式类和经典类
  • python3中没有经典类 统一新式类

什么是新式类?

继承object的类,以及它的子类都叫 新式类

什么是经典类?

没有继承object的类,以及它的子类都称之为经典类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 经典类:没有继承object的类,以及它的子类都称之为经典类
# py2
class Foo:
pass

class Bar(Foo):
pass

# py2 继承object的类,以及它的子类都叫 新式类
class Foo(object):
pass
class Bar(Foo):
pass

# py3中 统一是新式类 (默认继承object)
class Foo:
pass

属性查找之——MRO列表

属性查找方式有

  • 深度优先
  • 广度优先
1
2
3
当类是经典类时:在要查找的属性不存在时,会按照深度优先的方式查找下去

当类是新式类时:在要查找的属性不存在时,会按照广度优先的方式查找下去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class A(object):
def test(self):
print('from A')

class B(A):
def test(self):
print('from B')

class C(A):
def test(self):
print('from C')

class D(B):
def test(self):
print('from D')

class E(C):
def test(self):
print('from E')

class F(D,E):
# def test(self):
# print('from F')
pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类

新式类的内置mro方法(广度优先)

1
F.mro() # 查看属性查找的顺序

经典类没有提供属性深度优先的查找方法

Py003-01-01面向对象

编程范式

  • 面向过程(国民党的路线,一直在一次一次的解决问题,氪金,高级装备,最后败走台湾)
  • 面向对象(我党的路线,一直在建立统一思想,思想就是种信仰,为信仰而奋斗,从军人员的壮大,7月建党8月建军10月建国。你懂的)
1
2
3
4
生活实例: 从北京到上海
面向过程: 学驾照,买车,摇号,冲油,油门,百度地图,上海(你必须考证,买车,挂牌,会看地图)

面向对象: 找大巴车,火车,高铁,飞机 (你需要会开车吗?)

提到面向对象就不得不说的——类

  • 同类事物的抽象描述——类

类的定义和实例化对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 学生对象
# 001先定义类
class Student:
school = '清华'

def learn(self):
print('学习啦')

def eat(self):
print('吃饭啦')

def sleep(self):
print('睡觉啦')

# 最后产生对象
student_a = Student()

使用类

相关属性的增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 在类定义后 内部的代码就会执行 生成对应的名字空间(dict)
class Student:
# 数据属性
school = 'xx大学'
# 函数属性
def learn(self):
print('is learning')

print('=====running=====') # 会被执行


# 查看类的名字空间
print(Student.__dict__)
print(Student.__dict__['school'])
print(Student.__dict__['learn'])

# 查
print(Student.school)
print(Student.learn)

# 增 可以动态添加(如下);也可以在定义的时候添加属性如 school属性
Student.county = 'China'
print(Student.__dict__)
print(Student.county)

# 删
del Student.county

# 改
Student.school = '清华池'

如何使用对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# __init__方法用来为对象定制自己独有的特征

class Student:
# 数据属性
school = 'xx大学'

# __init__
def __init__(self,name,sex,age):
self.Name = name
self.Sex = sex
self.Age = age

# 函数属性
def learn(self):
print('is learning')

# 此时创建对象的时候 就不能直接 Student()了而是如下:
stu = Student('张三','男',18)

'''
__init__是你实例化的时候调用
1. 产生一个空对象 tmp
2. Student.__init__(tmp,'张三','男',18)
tmp.Name = '张三'
tmp.Sex = '男'
tmp.Age = 18
3. 返回tmp的地址给 stu
'''

对象属性的CRUD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查
stu = Student('张三','男',18)
print(stu.__dict__)
print(stu.Name)
print(stu.Age)

# 改
stu.Name = '李四'

# 删
del stu.Name

# 增
stu.className = '前端部'

属性查找和绑定方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class Student:
school = 'xx大学'

def __init__(self,name,sex,age):
self.Name = name
self.Sex = sex
self.Age = age

def learn(self):
print('%sis learning'%self.Name)

# 后产生对象
stu1 = Student('张三','男',11)
stu2 = Student('李四','男',22)
stu3 = Student('王五','男',33)

# 对象:特征和技能的结合体
# 类一系列对象相似的特征和相似的技能的结合体


# 类中的数据属性:是所有对象共有的
print(Student.school,id(Student.school))
print(stu1.school,id(stu1.school))
print(stu2.school,id(stu2.school))
print(stu3.school,id(stu3.school))

'''
# 它们的id值都一样
xx大学 2644686064016
xx大学 2644686064016
xx大学 2644686064016
xx大学 2644686064016
'''

#----------------------------------------------------------
# 类中的函数属性:是绑定给对象使用的,绑定到不同的对象是不同的绑定方法,对象调用绑定方法时,把对象本身当作第一个参数传入也就是self
print(Student.learn)
print(stu1.learn)
print(stu2.learn)
print(stu3.learn)

'''
# 它们的learn方法 地址都不一样
<function Student.learn at 0x00000271C5AFAEA0>
<bound method Student.learn of <__main__.Student object at 0x00000271C5B045C0>>
<bound method Student.learn of <__main__.Student object at 0x00000271C5B04710>>
<bound method Student.learn of <__main__.Student object at 0x00000271C5B04748>>
'''

# Student.learn() 报错 因为默认有个self参数 没有传递 learn() missing 1 required positional argument: 'self'
Student.learn(123) # 继续报错
Student.learn(stu1) # 张三is learning
Student.learn(stu2) # 李四is learning
Student.learn(stu3) # 王五is learning

# 起始你可以这样调用
stu1.learn() # 张三is learning
stu2.learn() # 李四is learning
stu3.learn() # 王五is learning

类中的数据属性:

  • 是所有对象共有的

类中的函数属性:

  • 是绑定给对象使用的
  • 绑定到不同的对象是不同的绑定方法
  • 对象调用绑定方法时,把对象本身当作第一个参数传入也就是self

属性查找

  • 先从实例对象上找,没有去类找,在没有就报错
1
2
3
4
5
6
7
8
9
10
stu1.x = 'from stu1'
Student.x = 'from Student class'
print(stu1.x) # 从自身找 'from stu1'

# 如果对象stu1自身没有y属性 则从Student找
# stu1.y = 'from stu1'
Student.y = 'from Student class'
print(stu1.y) # 从Student找 'from Student class'

# 如果Student也没有 报错

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'''
编写学生类,产生一堆学生对象
要求:
有一个计数器(属性),统计共实例了多少个对象
'''
class Student:
school = 'xx大学'
count = 0
def __init__(self,name,sex,age):
self.Name = name
self.Sex = sex
self.Age = age
Student.count+=1

def learn(self):
print('%sis learning'%self.Name)


s1 = Student('a1','nan',11)
s2 = Student('a2','nan',11)
s3 = Student('a3','nan',11)
print(Student.count)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'''
模拟王者荣耀定义两个英雄
要求:
英雄要有攻击力,生命,名字
实例化两个英雄对象
英雄之间可以互相殴打,被殴打一方掉血,血量少于0则判断死亡
'''

class Hero:
def __init__(self,name,life_value,aggresivity):
self.name = name
self.life_value = life_value
self.aggresivity = aggresivity

def attack(self,enemy):
enemy.life_value -= self.aggresivity

a = Hero('亚瑟',100,50)
b = Hero('小乔',100,60)

a.attack(b)

Vue_222_vue路由实现原理

vue路由实现原理

第一步——前端必会之选项卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
x-tab{display: block;}
x-tab>.nav>li.active{background: red;}
x-tab>.content>li{display: none;}
x-tab>.content>li.active{display: block;}
</style>
</head>
<body>
<x-tab>
<ol class="nav">
<li class="active">tab1</li>
<li>tab2</li>
</ol>
<ul class="content">
<li class="active">content1</li>
<li>content2</li>
</ul>
</x-tab>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
// 路由第一步 选项卡
$('x-tab').on('click','.nav>li',(e)=>{
let $li = $(e.currentTarget)
$li.addClass('active')
.siblings().removeClass('active')
let index = $li.index()
$('.content>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
})
</script>
</body>
</html>

传说没有那个入行的前端15分钟写不出一个选项卡,而且还是用jq。

第二步——你早就知道的锚点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
x-tab{display: block;}
x-tab>.nav>li.active{background: red;}
x-tab>.content>li{display: none;}
x-tab>.content>li.active{display: block;}
</style>
</head>
<body>
<x-tab>
<ol class="nav">
<li class="active">tab1</li>
<li>tab2</li>
</ol>
<ul class="content">
<li class="active">content1</li>
<li>content2</li>
</ul>
</x-tab>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
// 路由第二步 通过锚点
let index = location.hash|| '#0'
index = index.substring(1)
$('.nav>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
$('.content>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
$('x-tab').on('click','.nav>li',(e)=>{
let $li = $(e.currentTarget)
$li.addClass('active')
.siblings().removeClass('active')
let index = $li.index()
location.hash = index
$('.content>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
})
</script>
</body>
</html>

代码很重复是不是,往下看吧!

第三步——使用锚点的优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
x-tab{display: block;}
x-tab>.nav>li.active{background: red;}
x-tab>.content>li{display: none;}
x-tab>.content>li.active{display: block;}
</style>
</head>
<body>
<x-tab>
<ol class="nav">
<li class="active">
<a href="#0">tab1</a>
</li>
<li> <a href="#1">tab2</a></li>
</ol>
<ul class="content">
<li class="active">content1</li>
<li>content2</li>
</ul>
</x-tab>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
// 路由第二步 通过锚点
function selectTab(){
let index = location.hash|| '#0'
index = index.substring(1)
$('.nav>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
$('.content>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
}
selectTab()
// 路由第三步 监听锚点改变的事件
window.onhashchange = (e)=>{
selectTab()
}
</script>
</body>
</html>

hash的缺陷就是很容易被篡改和影响 如一个回到顶部的锚点 #top 路由状态就改了 不是你之前的index了

第四步——H5新的api——history

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
x-tab{display: block;}
x-tab>.nav>li.active{background: red;}
x-tab>.content>li{display: none;}
x-tab>.content>li.active{display: block;}
</style>
</head>
<body>
<x-tab>
<ol class="nav">
<li class="active">
<a href=./tab1>tab1</a>
</li>
<li> <a href="./tab2">tab2</a></li>
</ol>
<ul class="content">
<li class="active">content1</li>
<li>content2</li>
</ul>
</x-tab>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
selectTab()
// 路由第四步 不用锚点 用a标签的href跳转 但是我们不想跳转页面,所以就把事件阻止掉
// 目标就是a标签点击之后不跳转
$('x-tab').on('click','.nav > li > a',(e)=>{
e.preventDefault()
let a = e.currentTarget
let aHref = a.getAttribute('href')
console.log(aHref)
// 最重要一步使用 History的新api pushState
window.history.pushState({},'xxx',aHref)
selectTab()
})
// 监听


function selectTab(){
let index = location.pathname.substring(1)|| tab1
index = index.substring(3) - 1||0
console.log(index)
$('.nav>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
$('.content>li').eq(index)
.addClass('active')
.siblings().removeClass('active')
}

</script>
</body>
</html>

这样的方式你把外链分享出去是无法访问的必须服务器进行路径匹配。

1
2
3
4
// 后端代码
if(path==='/'||path==='/tab1'||path==='/tab2){
// 这里返回同一页面就ok
}

Py002-02-11正则re

re模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re

str = '''
张三 20 广州 173 50 13613133344
张四 30 深州 173 50 13813133344
张五 25 天津 173 50 15613133366
张六 13 北京 173 50 13613133388
张七 44 宁波 173 50 18213133377
张八 33 浙江 173 50 13013133341
张九 90 杭州 173 50 13813133312
张二 10 山西 173 50 13913133315
张一 22 大同 173 50 05713133344
'''

# 查找手机号

# 只找到第一个
print(re.search("[0-9]{11}",str))

# 查找所有匹配的手机号
print(re.findall("1[1-9]{10}",str))

常用正则规则

正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'.'     默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$' 匹配字符结尾, 若指定flags MULTILINE ,re.search('foo.$','foo1\nfoo2\n',re.MULTILINE).group() 会匹配到foo1
'*' 匹配*号前的字符0次或多次, re.search('a*','aaaabac') 结果'aaaa'
'+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?' 匹配前一个字符1次或0次 ,re.search('b?','alex').group() 匹配b 0次
'{m}' 匹配前一个字符m次 ,re.search('b{3}','alexbbbs').group() 匹配到'bbb'
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配, re.search("(abc){2}a(123|45)", "abcabca456c").group() 结果为'abcabca45'


'\A' 只从字符开头匹配同(^),re.search("\Aabc","alexabc") 是匹配不到的,相当于re.match('abc',"alexabc") 或^
'\Z' 匹配字符结尾,同$
'\d' 匹配数字0-9
'\D' 匹配非数字
'\w' 匹配[A-Za-z0-9]
'\W' 匹配非[A-Za-z0-9]
'\s' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'

'(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}

re的匹配语法有以下几种

  • re.match 从头开始匹配
  • re.search 匹配包含
  • re.findall 把所有匹配到的字符放到以列表中的元素返回
  • re.split 以匹配到的字符当做列表分隔符
  • re.sub 匹配字符并替换
  • re.fullmatch 全部匹配

match

1
2
3
4
5
6
7
8
9
10
# 从头匹配
a = re.match('[0-9]','abcde2') # None
b = re.match('[0-9]','1abcd2') # <_sre.SRE_Match object; span=(0, 1), match='1'>
c = re.match('[0-9]','ab3cd2') # None


# 如何取到匹配返回的值呢?
# 因为返回值可能是None 所以要做判断
if b:
print(b.group())

search

1
2
3
4
5
6
7
8
9
# 全局匹配
a = re.search('[0-9]','abcdef') # [] ['1'] ['3']
b = re.search('[0-9]','1abcd2') # <_sre.SRE_Match object; span=(0, 1), match='1'>
c = re.search('[0-9]','ab3cd2') # <_sre.SRE_Match object; span=(2, 3), match='3'>

# 如何取到匹配返回的值呢?
# 因为返回值可能是None 所以要做判断
if b:
print(b.group())

findall

1
2
3
4
# 
a = re.findall('[0-9]','abcde2') # ['2']
b = re.findall('[0-9]','1abcd2') # ['1', '2']
c= re.findall('[0-9]','ab3cd2') # ['3', '2']

使用实例

1
2
3
4
5
6
7
8
9
10
11
12
# 这俩效果一样  从头匹配
re.search('^ab','abc')

re.match('ab','abc')

# 从尾匹配
re.search('b$','ab')

# 注意 $ 不要跟match一起用
re.match('b$','b') # 能匹配
re.match('b.b$','bob') # 能匹配
re.match('b$','ab') # 不匹配

分组匹配

1
2
3
4
5
6
7
8
# 匹配 单词 和数字
re.search('[a-z]+[0-9]+','hjx123').group() # 'hjx123'

# 把 单词 和 数字分开
re.search('([a-z]+)([0-9]+)','hjx123').group() # 'hjx123'

# 再看
re.search('([a-z]+)([0-9]+)','hjx123').groups() # ('hjx','123')

非常炫酷的例子

1
2
3
4
5
6
7
8
9
# 模拟身份证号
s = '130704200005020613'
res = re.search('(?P<province>\d{3})(?P<city>\d+{3})(?P<born_year>\d{4})',s)
# 只要涉及分组就 groups
res.groups() # ('130','704','2000')

# 还能更NB
res.groupdict()
{"province":"130","city":"704","born_year":"2000"}

splitall 和 split

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 按数字分割
s = 'hjx22jack23rain31jin50'
re.split('\d',s) # ['hjx','','jack','','rain','','jin','']

re.split('\d+',s) # ['hjx','jack','rain','jin',''] 最后一个50后边又被分割出了


# 数字 或者 # 或者 - 分割
s2 = 'hjx22jack23rain31jin50#mock-Oldboy'
re.split('\d+|#|-',s2) # ['hjx', 'jack', 'rain', 'jin', '', 'mock', 'Oldboy']]

# 如果是 |分割的呢? 就 \|转义

# 如果字符是 \分割 在系统里会变成\\ 所以转义的时候要 \\\\
s3 = 'hjx22jack23ra\in31jin50#mock-Oldboy'
res3 = re.split('\d+|#|-|\\\\',s3)

split更强大的用法

1
2
3
4
5
6
s='9-2*5/3+7/3*99/4*2998+10*568/14'
re.split('[\*\-\/\+]',s)
# ['9', '2', '5', '3', '7', '3', '99', '4', '2998', '10', '568', '14']

re.split('[\*\-\/\+]',s,3)
# ['9', '2', '5', '3+7/3*99/4*2998+10*568/14']

sub

用于替换匹配的字符串

1
2
3
4
s = 'hjx22jack23ra\in31jin50#mock-Oldboy'
res = re.sub('\d','_',s) # hjx__jack__ra\in__jin__#mock-Oldboy

res2 = re.sub('\d','_',s,count=2) # hjx__jack23ra\in31jin50#mock-Oldboy

re.fullmatch(pattern, string, flags=0)

整个字符串匹配成功就返回re object, 否则返回None

1
re.fullmatch('\w+@\w+\.(com|cn|edu)',"alex@oldboyedu.cn")

最后一个

re.compile(pattern, flags=0)

1
2
3
4
5
prog = re.compile(正则) # 相当于把 正则 转换为代码规则 
result = prog.match(string) # 这样可以省略 正则规则转换的过程

以上俩行等价于
result = re.match(正则, string)

Flags标志符

  • re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
  • M(MULTILINE): 多行模式,改变’^’和’$’的行为
  • S(DOTALL): 改变’.’的行为,make the ‘.’ special character match any character at all, including a newline; without this flag, ‘.’ will match anything except a newline.
  • X(re.VERBOSE) 可以给你的表达式写注释,使其更可读,下面这2个意思一样
1
2
3
re.search('a','alex',re.I) # 忽略大小写

re.search('.','\n',re.S) # 匹配任意字符 . 是除了换行符 re.S代表任何字符