def select_columns(self, column_names):
        """
        Selects all columns where the name of the column or the type of column
        is included in the column_names. An exception is raised if duplicate columns
        are selected i.e. sf.select_columns(['a','a']), or non-existent columns
        are selected.

        Throws an exception for all other input types.

        Parameters
        ----------
        column_names: list[str or type]
            The list of column names or a list of types.

        Returns
        -------
        out : SFrame
            A new SFrame that is made up of the columns referred to in
            ``column_names`` from the current SFrame.

        See Also
        --------
        select_column

        Examples
        --------
        >>> sf = turicreate.SFrame({'user_id': [1,2,3],
        ...                       'user_name': ['alice', 'bob', 'charlie'],
        ...                       'zipcode': [98101, 98102, 98103]
        ...                      })
        >>> # This line is equivalent to `sf2 = sf[['user_id', 'zipcode']]`
        >>> sf2 = sf.select_columns(['user_id', 'zipcode'])
        >>> sf2
        +---------+---------+
        | user_id | zipcode |
        +---------+---------+
        |    1    |  98101  |
        |    2    |  98102  |
        |    3    |  98103  |
        +---------+---------+
        [3 rows x 2 columns]
        """
        if not _is_non_string_iterable(column_names):
            raise TypeError("column_names must be an iterable")
        if not (all([isinstance(x, six.string_types) or isinstance(x, type) or isinstance(x, bytes)
                     for x in column_names])):
            raise TypeError("Invalid key type: must be str, unicode, bytes or type")

        requested_str_columns = [s for s in column_names if isinstance(s, six.string_types)]

        # Make sure there are no duplicates keys
        from collections import Counter
        column_names_counter = Counter(column_names)
        if (len(column_names)) != len(column_names_counter):
            for key in column_names_counter:
                if column_names_counter[key] > 1:
                    raise ValueError("There are duplicate keys in key list: '" + key + "'")

        colnames_and_types = list(zip(self.column_names(), self.column_types()))

        # Ok. we want the string columns to be in the ordering defined by the
        # argument.  And then all the type selection columns.
        selected_columns = requested_str_columns
        typelist = [s for s in column_names if isinstance(s, type)]

        # next the type selection columns
        # loop through all the columns, adding all columns with types in
        # typelist. But don't add a column if it has already been added.
        for i in colnames_and_types:
            if i[1] in typelist and i[0] not in selected_columns:
                selected_columns += [i[0]]

        selected_columns = selected_columns

        with cython_context():
            return SFrame(data=[], _proxy=self.__proxy__.select_columns(selected_columns))