天天看點

information hiding

Use naming conventions to make data attributes and methods invisible outside the class

Convention: Begin name with __ but do not end with it

class InfoHiding(object):

    def __init__(self):
        self.visible = 'Look at me'
        self.__visible__ = 'Look at me too'
        self.__invisible = 'Do not look at me directly'

    def print_visible(self):
        print(self.visible)

    def print_invisible(self):
        print(self.__invisible)

    def __invisible_print_invisible(self):# a invisible method trying to print invisible
        print(self.__invisible)

    def __visible_print_invisible__(self):# a visible method  trying to print invisible
        print(self.__invisible)

test = InfoHiding()

print(test)
print(test.visible)
print(test.__visible__)
print(test.__invisible)


test.print_visible()
test.print_invisible()
test.__invisible_print_invisible()
test.__visible_print_invisible__()

# Infomation Hiding and Subclasses
class SubClass(InfoHiding):
    def __init__(self):
        InfoHiding.__init__(self)
        print(self.__invisible)

sub_test = SubClass()

# In practice, Infomation Hiding Convention in Python is Rarely Used
#     without it users may rely on attributes that are not necessarily part of the specification of the class
#     without it users may also change these attributes in undersirable ways

class Course(object):

    def __init__(self, student_list):
        self.students = student_list
        self.grades = {}

    def get_students():
        return self.students[:]

course1 = Course([1, 2, 3])
course2 = Course([4, 5, 6])

all_students = course1.students
all_students.extend(course2.students)
#Solution: Use get_student method and never access the attribute directly; Or return a copy of the list
print(course1.students)

           

In Practice, Information Hiding in Python Requires Discipline’

Do not directly access data attributes from outside the class in which they are defined

Return copies of mutable objects rather than the objects themselves

# Speed Considerations
class Course(object):

    def __init__(self, student_list):
        self.students = student_list
        self.grades = {}

    def get_students(self):
        return self.students[:] # Create a copy of a list  already in memory

    def add_grade(self, student, grade):
        self.grades[student] = grade

course1 = Course([i for i in range(1, 11)])
for i in course1.get_students():
    course1.add_grade(i, 100)

print(course1.get_students())

# Generators with yield
class Course(object):

    def __init__(self, student_list):
        self.students = student_list
        self.grades = {}

    def get_students(self):
        for i in self.students:
            yield i

    def add_grade(self, student, grade):
        self.grades[student] = grade

    def get_grade(self):
        return(self.grades)


course1 = Course([i for i in range(1, 11)])
for i in course1.get_students():
    course1.add_grade(i, 100)

print(course1.get_students())
print(course1.get_grade())

'9) When to Use Classes'
# Methods require look-up so are a bit slower than functions
# Designing classes properly can be quite time-consuming
# For building a reusable and extendable code, Use classes