绑定方法
对象的绑定方法
在类中没有被任何装饰器修饰的方法就是 绑定到对象的方法,这类方法专门为对象定制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Person: country = "China"
def __init__(self, name, age): self.name = name self.age = age
def speak(self): print(self.name + ', ' + str(self.age))
p = Person('Kitty', 18)
print(p.__dict__) print(Person.__dict__['speak'])
|
speak即为绑定到对象的方法,这个方法不在对象的名称空间中,而是在类的名称空间中。
通过对象调用绑定到对象的方法,会有一个自动传值的过程,即自动将当前对象传递给方法的第一个参数(self,一般都叫self,也可以写成别的名称);若是使用类调用,则第一个参数需要手动传值。
1 2 3 4 5 6 7
| p = Person('Kitty', 18)
p.speak()
Person.speak(p)
|
类的绑定方法
类中使用 @classmethod 修饰的方法就是绑定到类的方法。这类方法专门为类定制。通过类名调用绑定到类的方法时,会将类本身当做参数传给类方法的第一个参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Operate_database(): host = '192.168.0.5' port = '3306' user = 'abc' password = '123456'
@classmethod def connect(cls): print(cls) print(cls.host + ':' + cls.port + ' ' + cls.user + '/' + cls.password)
Operate_database.connect() ''' <class '__main__.Operate_database'> 192.168.0.5:3306 abc/123456 '''
|
通过对象也可以调用,只是默认传递的第一个参数还是这个对象对应的类。
1 2 3 4 5
| Operate_database().connect() ''' <class '__main__.Operate_database'> 192.168.0.5:3306 abc/123456 '''
|
非绑定方法
在类内部使用 @staticmethod 修饰的方法即为非绑定方法,这类方法和普通定义的函数没有区别,不与类或对象绑定,谁都可以调用,且没有自动传值的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import hashlib
class Operate_database(): def __init__(self, host, port, user, password): self.host = host self.port = port self.user = user self.password = password
@staticmethod def get_passwrod(salt, password): m = hashlib.md5(salt.encode('utf-8')) m.update(password.encode('utf-8')) return m.hexdigest()
hash_password = Operate_database.get_passwrod('lala', '123456') print(hash_password)
p = Operate_database('192.168.0.5', '3306', 'abc', '123456') hash_password = p.get_passwrod(p.user, p.password) print(hash_password)
|
简而言之,非绑定方法就是将普通方法放到了类的内部。
练习
假设我们现在有一个需求,需要让Mysql实例化出的对象可以从文件settings.py中读取数据。
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
| IP = '1.1.1.10' PORT = 3306 NET = 27
import uuid
class Mysql: def __init__(self, ip, port, net): self.uid = self.create_uid() self.ip = ip self.port = port self.net = net
def tell_info(self): """查看ip地址和端口号""" print('%s:%s' % (self.ip, self.port))
@classmethod def from_conf(cls): return cls(IP, NET, PORT)
@staticmethod def func(x, y): print('不与任何人绑定')
@staticmethod def create_uid(): """随机生成一个字符串""" return uuid.uuid1()
obj = Mysql('10.10.0.9', 3307, 27) obj.tell_info()
|
绑定方法小结
如果函数体代码需要用外部传入的类,则应该将该函数定义成绑定给类的方法
如果函数体代码需要用外部传入的对象,则应该将该函数定义成绑定给对象的方法
1 2 3 4 5 6 7
| obj1 = Mysql.from_conf() obj1.tell_info()
print(obj.tell_info)
print(obj.from_conf)
|
非绑定方法小结
如果函数体代码既不需要外部传入的类也不需要外部传入的对象,则应该将该函数定义成非绑定方法/普通函数
1 2 3 4 5 6 7 8 9 10
| obj.func(1, 2)
Mysql.func(3, 4)
print(obj.func)
print(Mysql.func)
print(obj.uid)
|
面向对象进阶小结
类的继承
继承父类,则会有父类的所有属性和方法
1 2 3 4 5 6 7 8
| class ParentClass1(): pass class ParentClass2(): pass
class SubClass(ParentClass1,ParentClass2): pass
|
类的派生
继承父类的同时自己有init,然后也需要父类的init
1 2 3 4 5 6 7 8 9 10
| class ParentClass1(): def __init__(self,name): pass
class SubClass(ParentClass): def __init__(self,age): self.age = age
|
类的组合
类对象可以引用/当做参数传入/当做返回值/当做容器元素,类似于函数对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class ParentClass1(): count = 0 def __init__(self,name): pass
class SubClass(ParentClass): def __init__(self,age): self.age = age
pc = ParentClass1() sc = SubClass()
sc.parent_class = pc sc.parent_class.count
|
菱形继承问题
新式类:继承object的类,python3中全是新式类
经典类:没有继承object的类,只有python2中有
在菱形继承的时候,新式类是广度优先(老祖宗最后找);经典类深度优先(一路找到底,再找旁边的)
多态与多态性
一种事物的多种形态,动物–>人/猪/狗
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): @abc.abstractmethod def eat(): print('eat')
class People(Animal): def eat(): pass
class Pig(Animal): def eat(): pass def run(): pass
class Dog(Animal): def run(): pass
peo = People() peo.eat() peo1 = People() peo1.eat() pig = Pig() pig.eat()
def func(obj): obj.eat()
class Cat(Animal): def eat(): pass cat = Cat()
func(cat)
|
鸭子类型:只要长得像鸭子,叫的像鸭子,游泳像鸭子,就是鸭子
类的封装
隐藏属性,只有类内部可以访问,类外部不可以访问
1 2 3 4 5 6 7 8 9
| class Foo(): __count = 0 def get_count(self): return self.__count f = Foo() f.__count f._Foo__count
|
类的property特性
把方法变成属性引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class People(): def __init__(self,height,weight): self.height = height self.weight = weight @property def bmi(self): return weight/(height**2) @bmi.setter def bmi(self,value) print('setter') @bmi.deleter def bmi(self): print('delter')
peo = People peo.bmi
|
类与对象的绑定方法和非绑定方法
没有任何装饰器装饰的方法就是对象的绑定方法, 类能调用, 但是必须得传参给self
被 @classmethod 装饰器装饰的方法是类的绑定方法,参数写成cls, cls是类本身, 对象也能调用, 参数cls还是类本身
被 @staticmethod 装饰器装饰的方法就是非绑定方法, 就是一个普通的函数
isinstance与type
在游戏项目中,我们会在每个接口验证客户端传过来的参数类型,如果验证不通过,返回给客户端“参数错误”错误码。
这样做不但便于调试,而且增加健壮性。因为客户端是可以作弊的,不要轻易相信客户端传过来的参数。
验证类型用type函数,非常好用,比如
1 2 3
| print(type('foo') == str)
print(type(2.3) in (int, float))
|
既然有了type()来判断类型,为什么还有isinstance()呢?
一个明显的区别是在判断子类。
type()不会认为子类是一种父类类型;isinstance()会认为子类是一种父类类型。
千言不如一码。
1 2 3 4 5 6 7 8 9 10 11 12
| class Foo(object): pass class Bar(Foo): pass print(type(Foo()) == Foo)
print(type(Bar()) == Foo)
print(isinstance(Bar(),Foo))
|
需要注意的是,旧式类跟新式类的type()结果是不一样的。旧式类都是<type ‘instance’>。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class A: pass class B: pass class C(object): pass
print('old style class',type(A()))
print('old style class',type(B()))
print('new style class',type(C()))
print(type(A()) == type(B()))
|
注意:不存在说isinstance比type更好。只有哪个更适合需求。
issubclass
1 2 3 4 5 6 7 8 9
| class Parent: pass
class Sub(Parent): pass
print(issubclass(Sub, Parent)) print(issubclass(Parent, object))
|