面向对象
类有两种属性:属性引用和实例化
属性引用(类名.属性)
class Person: #定义一个人类
role = 'person' #人的角色属性都是人
def walk(self): #人都可以走路,也就是有一个走路方法
print("person is walking...")
print(Person.role) #查看人的role属性
print(Person.walk) #引用人的走路方法,注意,这里不是在调用
实例化:类名加括号就是实例化,会自动出发__init__函数的运行,可以用它为每个实例定制自己的特性
类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
二:特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
面向对象小结:
class 类名:
def __init__(self,参数1,参数2):
self.对象的属性1 = 参数1
self.对象的属性2 = 参数2
def 方法名(self):pass
def 方法名2(self):pass
对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西
#类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
#括号里传参数,参数不需要传self,其他与init中的形参一一对应
#结果返回一个对象
对象名.对象的属性1 #查看对象的属性,直接用 对象名.属性名 即可
对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可
对象之间的交互:
1 class Person: # 定义一个人类
2 role = 'person' # 人的角色属性都是人
3
4 def __init__(self, name, aggressivity, life_value):
5 self.name = name # 每一个角色都有自己的昵称;
6 self.aggressivity = aggressivity # 每一个角色都有自己的攻击力;
7 self.life_value = life_value # 每一个角色都有自己的生命值;
8
9 def attack(self,dog):
10 # 人可以攻击狗,这里的狗也是一个对象。
11 # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降
12 dog.life_value -= self.aggressivity
13
14 class Dog: # 定义一个狗类
15 role = 'dog' # 狗的角色属性都是狗
16
17 def __init__(self, name, breed, aggressivity, life_value):
18 self.name = name # 每一只狗都有自己的昵称;
19 self.breed = breed # 每一只狗都有自己的品种;
20 self.aggressivity = aggressivity # 每一只狗都有自己的攻击力;
21 self.life_value = life_value # 每一只狗都有自己的生命值;
22
23 def bite(self,people):
24 # 狗可以咬人,这里的狗也是一个对象。
25 # 狗咬人,那么人的生命值就会根据狗的攻击力而下降
26 people.life_value -= self.aggressivity
27
28 p = Person('alex', 10, 100)
29 d = Dog('erha', 'big_dog', 10, 100)
30 print(d.life_value) # 查看d的生命值
31 p.attack(d) # p打了d
32 print(d.life_value) # d掉了10点血
33
1 from math import pi
2
3 class Circle:
4 '''
5 定义了一个圆形类;
6 提供计算面积(area)和周长(perimeter)的方法
7 '''
8 def __init__(self,radius):
9 self.radius = radius
10
11 def area(self):
12 return pi * self.radius * self.radius
13
14 def perimeter(self):
15 return 2 * pi *self.radius
16
17
18 circle = Circle(10) #实例化一个圆
19 area1 = circle.area() #计算圆面积
20 per1 = circle.perimeter() #计算圆周长
21 print(area1,per1) #打印圆面积和周长
面向对象的组合方法
1 class BirthDate:
2 def __init__(self,year,month,day):
3 self.year=year
4 self.month=month
5 self.day=day
6
7 class Couse:
8 def __init__(self,name,price,period):
9 self.name=name
10 self.price=price
11 self.period=period
12
13 class Teacher:
14 def __init__(self,name,gender,birth,course):
15 self.name=name
16 self.gender=gender
17 self.birth=birth
18 self.course=course
19 def teach(self):
20 print('teaching')
21
22 p1 = Teacher('alex', 'male',
23 BirthDate('1996', '1', '27'),
24 Course('python', '180000', '1 year')
25 )
26
27 print(p1.birth.year, p1.birth.month, p1.birth.day)
28 # 1996 1 27
29 print(p1.course.name, p1.course.price, p1.course.period)
30 # python 180000 1 year
面向对象的三大特性
继承 多态 封装
1. 继承
一个类可以被多个类继承
一个类可以继承多个父类
1 class Animal:
2 def __init__(self):
3 print('执行Animal.__init__')
4 self.func()
5 def eat(self):
6 print('%s eating'%self)
7 def drink(self):
8 print('%s drinkingg' % self)
9 def func(self):
10 print('Animal.func')
11
12 class Dog(Animal):
13 def guard(self):
14 print('guarding')
15 def func(self):
16 print('Dog.func')
17 dog = Dog() # 执行Animal.__init__ Dog.func
注意:
父类中没有的属性(方法), 在子类中出现,叫做派生属性(方法)
只要是子类的对象调用,子类中有的名字,一定要用子类的,子类中没有才找父类的,若父类也没用,则报错
如果还想要用父类的,则单独调用父类:
父类名.方法名 需要自己传参数
super.().方法名 不需要自己传参数
1 class Animal:
2 def __init__(self, name, aggr, hp):
3 self.name = name
4 self.aggr = aggr
5 self.hp = hp
6 def eat(self):
7 print('eating!!!')
8
9 class Dog(Animal):
10 def __init__(self, name, aggr, hp, kind): # 定义自己属性
11 # 方法1:直接用父类调用
12 Animal.__init__(self, name, aggr, hp)
13 self.kind = kind # 派生属性
14 class Person(Animal):
15 def __init__(self, name, aggr, hp, sex):
16 # 方法2: super指定父类
17 super().__init__(name, aggr, hp)
18 self.sex = sex # 派生属性
19 self.money = 0
20
21 jin = Dog('金老板',100,500,'泰迪')
22 print(jin.name)
23 super(Dog, jin).eat() # super在外部调用,必须传类名和对象名
总结:
继承
单继承
先抽象再继承,几个类之间的相同代码抽象出来,成为父类
子类自己没有的名字,就可以使用父类的方法和属性
如果子类自己有,一定是先用自己的
在类中使用self的时候,一定要看清楚self指向谁
多继承
新式类和经典类:
多继承寻找名字顺序:新式类广度优先,经典类深度优先
新式类中有一个类名.mro()查看广度优先的继承顺序
python3中 有一个super()方法,根据广度优先的顺序查找上一个类
2. 多态 : python天生支持多态
3. 封装
原则: 将不需要对外提供的内容都隐藏起来
把属性都隐藏,提供公共方法对其访问
私有变量的私有方法
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
class A:
__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
def __init__(self):
self.__X=10 #变形为self._A__X
def __foo(self): #变形为_A__foo
print('from A')
def bar(self):
self.__foo() #只有在类内部才可以通过__foo的形式访问到.
1 class Person:
2 __key = 123
3 def __init__(self, name, passwd):
4 self.name = name
5 self.__passwd = passwd # 私有变量
6
7
8 def get_pwd(self):
9 return self.__passwd # 只要在类的内部使用私有属性,就会自动的带上_类名
10 alex = Person('alex', 'alex3714')
11 print(alex._Person__passwd) # 对象._类名__属性名
注:
所有的私有 都是在变量的左边加上双下划线
# 对象的私有属性
# 类中的私有方法
# 类中的静态属性
所有的私有的 都不能在类的外部使用
property属性
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
1 from math import pi
2 class Circle:
3 def __init__(self, r):
4 self.r = r
5
6 @property # 伪装成属性
7 def perimeter(self):
8 return 2*pi*self.r
9
10 def area(self):
11 return self.r**2*pi
12 c1 = Circle(5)
13 print(c1.area()) # 伪 圆的面积
14 print(c1.perimeter) # 装成属性 被@property伪装成属性 调用该方法名时候 不用加()
为什么要使用property?
将一个类的函数定义成property属性后,对象再去使用obj.area的时候根本无法察觉自己的area是执行了一个函数计算出来的,这种特性遵循了统一访问的原则
面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开
一个静态属性property本质就是实现了get,set,delete三种方法
1 class Goods:
2
3 def __init__(self):
4 # 原价
5 self.original_price = 100
6 # 折扣
7 self.discount = 0.8
8
9 @property
10 def price(self):
11 # 实际价格 = 原价 * 折扣
12 new_price = self.original_price * self.discount
13 return new_price
14
15 @price.setter # set时候自动运行
16 def price(self, value):
17 self.original_price = value
18
19 @price.deleter # 删除的时候自动运行
20 def price(self):
21 del self.original_price
22
23
24 obj = Goods()
25 obj.price # 获取商品价格
26 obj.price = 200 # 修改商品原价
27 print(obj.price)
28 del obj.price # 删除商品原价
classmethod 类方法
1 class Goods:
2 __discount = 0.8
3 def __init__(self, name, price):
4 self.name = name
5 self.__price = price
6 @property # @property装饰器装饰后,调用该方法后面不用加()
7 def price(self):
8 return self.__price*Goods.__discount
9
10 @classmethod # 把一个方法变成一个类中的方法,这个方法就可以被类调用,不需要依托任何对象
11 def change_discount(cls, new_discount): # 修改折扣
12 cls.__discount = new_discount
13 apple = Goods('苹果', 5)
14 print(apple.price) # 4.0
15 Goods.change_discount(0.5)
16 print(apple.price) # 2.5
17 # 当这个方法的操作只涉及静态属性的时候,就应该使用classmethod来装饰这个方法
staticmethod方法
class Staticmethod_Demo():
role = 'dog'
@staticmethod
def func():
print("当普通方法用")
Staticmethod_Demo.func()
# 在完全面向对象的程序中,如果一个函数既和对象没有关系,也和类没有关系,那么就用staticmethod将这个函数变成一个静态方法
面向对象的进阶
isinstance和issubclass
isinstance 判断类和对象的关系
issubclass 判断子类和父类的关系
hasattr getattr setattr delattr 四个实现自省的函数
1 class Foo:
2 f = '类的静态变量'
3 def __init__(self,name,age):
4 self.name=name
5 self.age=age
6
7 def say_hi(self):
8 print('hi,%s'%self.name)
9
10 obj=Foo('egon',73)
11
12 #检测是否含有某属性
13 print(hasattr(obj,'name'))
14 print(hasattr(obj,'say_hi'))
15
16 #获取属性
17 n=getattr(obj,'name')
18 print(n)
19 func=getattr(obj,'say_hi')
20 func() # hi, egon
21
22 #设置属性
23 setattr(obj,'sb',True)
24 setattr(obj,'show_name',lambda self:self.name+'nb')
25 print(obj.__dict__)
26 print(obj.show_name(obj)) # egonnb
27
28 #删除属性
29 delattr(obj,'age')
30 delattr(obj,'show_name')
31
32 注:反射:是用字符串类型的名字 去操作变量
类的内置方法
# __str__ :打印一个对象的时候,就是调用类名.__str__
class Teacher:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def __str__(self):
return "Teacher's object:%s"%self.name
def __repr__(self):
return str(self.__dict__)
nezha = Teacher('nezha',1200)
print(nezha) # Teacher's object:nezha
print('%s'%nezha) # Teacher's object:nezha
print(str(nezha)) # Teacher's object:nezha
print('%r'%nezha) # {'name': 'nezha', 'salary': 1200}
print(repr(nezha)) # {'name': 'nezha', 'salary': 1200}
# %s str()直接打印 实际上都是走的__str__
# %r repr() 都是走的 __repr__
# 如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类的__str__
# repr()只会找__repr__,如果没有就找父类
__del__
class Foo:
def __del__(self):
print('执行我啦')
f1=Foo()
del f1 # 执行我啦
# 析构方法,当对象在内存中被释放时,自动触发执行
__call__
对象后面加括号,触发执行。
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()


浙公网安备 33010602011771号