Python Class
Class Definition
- class <name>(<parents>): the common 'Object' as a parent
- static member variables- just state outside constructor
- class member varaibles
- with same name, instance member will shadow the class member
>>> class Student(object): ... name = 'Student' ... >>> s = Student() >>> print(s.name) Student
- def __init__(self, vars): _constructor
- private members: default is public
- double underline __
- indication for private variable
- _var will be replaced by _classname__var, preventing conflict of same name in base and derived class
- single underline _
- to prevent attributes be loaded by "from module import"
- double underline __
- def name(self, vars): member functions
- usually modify the class (self)
- class member function - the class method: @classmethod decorator
- the member function does not modify self
- static member function: @staticmethod decorator
- operator overload
- __eq___
Property and Setter
@property and @<attribute>.setter decorates the function to be getter and setter
Inheritance
- must call parent constructor in init() explicitly
- child constructor -> parent contructor
- Support multiple inheritance
- isinstance(A,B) - is A an Instance of B
- use super() to avoid multiple constructor call
- if needs to call multiple constructors, must use parent._init__() method to explicitly call the second parent
- use C3 linearization for method resolution
- MixIn
- achieve different functions through multiple inheritance
- isinstance(A,B) - is A an Instance of B
- Abstract method: @abstractmethod decorator
Methods on classes
- isinstance(a, class name)
- judge inheritance
- type()
- class type
- types module
- include function types, generation types, etc
- dir()
- show all attributes and methods
- getattr(<obj>, str), setattr(), hasattr()
- throw AttributeError is not found
Dynamic Programming Language
vs Java(static language)
- object shares same methods will be treated likely
bind attributes and methods to instances freely
class Student(object): def __init__(self, name): self.name = name s = Student('Bob') s.score = 90 def set_age(self, age): self.age = age from types import MethodType s.set_age = MethodType(set_age, s)
__slots__: special variable to define the list of attributes we allow to bind
- does not work on children
class Student(object): __slots__ = ('name', 'age')
customized class methods
- __str__
- use by print()
- \_repr___
- used by call func name, usually can be the same as above
- __len\__
__iter\_ and __next___
- used by for ... in
- will call \_next__()_ to get next one
class Fib(object): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: raise StopIteration() return self.a
__getitem\__
- supports random access (list-like object)
- usually needs to support int or slice type of inputs
\_getattr__(self, attr)_
- the way python gets attributes
- can be over-written to suppress attribute errors of parse commands
class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): return Chain('%s/%s' % (self._path, path)) def __str__(self): return self._path __repr__ = __str__
__call\__(self)_
- directly calls the object instance
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name) >>> s = Student('Michael') >>> s() My name is Michael.- callable(<object>)
- if a object is a Callable
Examples
class Document():
# static variable/constant
WELCOME_STR = 'Welcome! The context for this book is {}.'
def __init__(self, title, author, context):
print('init function called')
self.title = title
self.author = author
self.__context = context
# class method
@classmethod
def create_empty_book(cls, title, author):
return cls(title=title, author=author, context='nothing')
# member function
def get_context_length(self):
return len(self.__context)
# static function
@staticmethod
def get_welcome(context):
return Document.WELCOME_STR.format(context)
empty_book = Document.create_empty_book('What Every Man Thinks About Apart from Sex', 'Professor Sheridan Simove')
print(empty_book.get_context_length())
print(empty_book.get_welcome('indeed nothing'))
########## output ##########
init function called
7
Welcome! The context for this book is indeed nothing.
from abc import ABCMeta, abstractmethod
class Entity(metaclass=ABCMeta):
def __init__(self, object_type):
print('parent class init called')
self.object_type = object_type
def get_context_length(self):
raise Exception('get_context_length not implemented')
@abstractmethod
def get_title(self):
pass
class Document(Entity):
def __init__(self, title, author, context):
print('Document class init called')
Entity.__init__(self, 'document')
self.title = title
self.author = author
self.__context = context
def get_context_length(self):
return len(self.__context)
class Video(Entity):
def __init__(self, title, author, video_length):
print('Video class init called')
Entity.__init__(self, 'video')
self.title = title
self.author = author
self.__video_length = video_length
def get_context_length(self):
return self.__video_length
harry_potter_book = Document('Harry Potter(Book)', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
harry_potter_movie = Video('Harry Potter(Movie)', 'J. K. Rowling', 120)
print(harry_potter_book.object_type)
print(harry_potter_movie.object_type)
harry_potter_book.print_title()
harry_potter_movie.print_title()
print(harry_potter_book.get_context_length())
print(harry_potter_movie.get_context_length())
########## output ##########
Document class init called
parent class init called
Video class init called
parent class init called
document
video
Harry Potter(Book)
Harry Potter(Movie)
77
120
Python Modularization
Some principles
- import once
- usually at the beginning of program
- use absolute file path
- don't use sys.path.append() to change running path
- Single Repository
- Why Google uses a single repo
- simplify dependency management
Set up independent running environments
two ways to change running path (the list interpretor looks for packges)
import sys print(sys.path) ########## output ########## ['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages'] # change path sys.path[0] = '/home/ubuntu/workspace/your_projects'# use Virtual Enviornment # in the activate file in the virtual environment export PYTHONPATH="/home/ubuntu/workspace/your_projects"- use the second one
If need running code in package (eg. printing) use if \_name__ == '__main__'_
MetaClass
Type
- return the type of the class
- instance type is the class
- class is of type 'type'
- class is an instance of 'type'
- can dynamically create new types without definition
>>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # create hello calss with attribute hello
Metaclass
- inherit type
- allows you to create and modify classes
- and modify the behaviors in class creation
- class is the instance of metaclass
- overwrites __new\_ _to modify class creation behaviors
- the type.\_new__(class, name, bases, attrs) _is called when python interpretor finds class definiions
MetaClass in Engineering
Example
ORM(Object-Relational Mapping)
# use case
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 创建一个实例:
u = User(id=12345, name='Michael', email='[email protected]', password='my-pwd')
# 保存到数据库:
u.save()
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__, self.name)
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint')
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
mappings = dict()
for k, v in attrs.items():
if isinstance(v, Field):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs)
lass Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
super(Model, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))